Tematem tego postu będzie połączenie układów ADC, DMA z wyświetlaczem wyposażonym w sterownik HD44780.
Wyświetlacz HD44780:
Potencjometr zostanie podłączony do pojedynczego kanału ADC, dołączonego do pinu PC2. Pozostałe dwa wyprowadzenia odpowiednio do zasilania 3,3V oraz GND.
Pierwszy program zawiera wyświetlanie uśrednionego parametru z ADC.
Drugi program działa w podobny sposób, z tą różnicą, że wykorzystuje on DMA do odczytania wartości podanej z potencjometru.
Do programu zawartego powyżej dodałem obsługę podanego kanału DMA. Wszystkie ważne elementy kodu zostały opisane bezpośrednio w programie głównym.
Podłączenie
Wyświetlacz HD44780:
- VSS - GND
- VDD - 5V
- V0 - potencjometr
- RS - PB2
- RW - GND
- E - PB7
- D0-D3 - NC
- D4 - PC12
- D5 - PC13
- D6 - PC14
- D7 - PC15
- A - 3V
- K - GND
Potencjometr zostanie podłączony do pojedynczego kanału ADC, dołączonego do pinu PC2. Pozostałe dwa wyprowadzenia odpowiednio do zasilania 3,3V oraz GND.
Programowanie:
Pierwszy program zawiera wyświetlanie uśrednionego parametru z ADC.
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_adc.h" #include <stdio.h> #include "stm32f4_delay.h" #include "stm32f4_HD44780.h" int WartoscPrzetwo = 0; // wartosc odczytana z ADC void KonfiguracjaADC(void); int KonwersjaADC(void); int main(void) { //Zdefiniowanie zmiennych char res[20]; int Pomocnicza = 0; int i=0; HD44780_Init(16, 2); KonfiguracjaADC(); while(1) { //Odczytanie przetworzonej wartosci WartoscPrzetwo = KonwersjaADC(); for(i=0; i<5; i++) { WartoscPrzetwo = KonwersjaADC(); Pomocnicza = Pomocnicza + WartoscPrzetwo; } sprintf(res, "%d", Pomocnicza/5); HD44780_Puts(0, 0, res); Delayms(1000); HD44780_Clear(); Pomocnicza = 0; } } void KonfiguracjaADC() { ADC_InitTypeDef ADC_Init_Structure; GPIO_InitTypeDef GPIO_Init_Structure; //Konfiguracja zegarów //ADC1 podlaczone do APB2 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //Zegar dla portu ADC RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOCEN, ENABLE); //Konfiguracja pinu analogowego //Kanal 10 podlaczony do PC0 GPIO_Init_Structure.GPIO_Pin = GPIO_Pin_2; GPIO_Init_Structure.GPIO_Mode = GPIO_Mode_AN; GPIO_Init_Structure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_Init_Structure); //Konfiguracja ADC ADC_DeInit(); //Przetworzone dane wyrównane do prawej ADC_Init_Structure.ADC_DataAlign = ADC_DataAlign_Right; //Dane wejsciowe przetwarzane w 12 bitowy numer z duza dokladnoscia, max 4096 ADC_Init_Structure.ADC_Resolution = ADC_Resolution_12b; //Konwersacja jest ciagla przetwarzanie wiecej niz jeden raz ADC_Init_Structure.ADC_ContinuousConvMode = ENABLE; ADC_Init_Structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //Bez wyzwalania zewnetrznego ADC_Init_Structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //Liczba konwersji ADC_Init_Structure.ADC_NbrOfConversion = 1; //Pomiar jednego kanalu, skanowanie kanalow zostalo wylaczone ADC_Init_Structure.ADC_ScanConvMode = DISABLE; //Inicjalizacja ADC ADC_Init(ADC1, &ADC_Init_Structure); //Wybranie kanalu z którego bedzie odczytywane //ADC kanal 10, GPIOC1, Czestotliwosc probkowania = 1Mhz ADC_RegularChannelConfig(ADC1,ADC_Channel_12,1,ADC_SampleTime_144Cycles); //Wlaczenie konwersji ADC ADC_Cmd(ADC1,ENABLE); } int KonwersjaADC() { //Rozpoczecie konwersji ADC_SoftwareStartConv(ADC1); //Przetwarzanie while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); //Zwrócenie przetworzonych danych return ADC_GetConversionValue(ADC1); }
Drugi program działa w podobny sposób, z tą różnicą, że wykorzystuje on DMA do odczytania wartości podanej z potencjometru.
Do programu zawartego powyżej dodałem obsługę podanego kanału DMA. Wszystkie ważne elementy kodu zostały opisane bezpośrednio w programie głównym.
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_adc.h" #include "stm32f4xx_dma.h" #include <stdio.h> #include "stm32f4_delay.h" #include "stm32f4_HD44780.h" uint32_t ADCValue[1] = {0}; void ADCInit(void); void DMAInit(void); int main(void) { //Deklaracja zmiennych char res[20]; int pomocnicza = 0; int i = 0; HD44780_Init(16, 2); ADCInit(); DMAInit(); while(1) { //Pobranie 10 wartosci z DMA for(i=0;i<10;i++) { pomocnicza = pomocnicza + ADCValue[0]; } //Wyciagniecie sredniej arytmetycznej pomocnicza = (pomocnicza / 10); //Wyswietlenie wyniku sprintf(res, "A:%d", pomocnicza); HD44780_Puts(0, 0, res); sprintf(res, "V:%.2f", ((3.3/4096.0) * (pomocnicza))); HD44780_Puts(0, 1, res); //Wyzerowanie zmiennej pomocnicza = 0; Delayms(500); HD44780_Clear(); } } void ADCInit() { //Definicja struktury ADC_InitTypeDef ADCInit; GPIO_InitTypeDef GPIOInit; //Konfiguracja zegarów RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //Zegar dla portu ADC RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOCEN, ENABLE); //Konfiguracja pinu analogowego //Kanal 12 podlaczony do PC2 GPIOInit.GPIO_Pin = GPIO_Pin_2; GPIOInit.GPIO_Mode = GPIO_Mode_AN; GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIOInit.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOC, &GPIOInit); //Konfiguracja ADC ADC_DeInit(); //Przetworzone dane wyrównane do prawej ADCInit.ADC_DataAlign = ADC_DataAlign_Right; //Dane wejsciowe przetwarzane w 12 bitowy numer z duza dokladnoscia, max 4096 ADCInit.ADC_Resolution = ADC_Resolution_12b; //Konwersacja jest ciagla przetwarzanie wiecej niz jeden raz ADCInit.ADC_ContinuousConvMode = ENABLE; ADCInit.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //Bez wyzwalania zewnetrznego ADCInit.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //Liczba konwersji ADCInit.ADC_NbrOfConversion = 16; //Pomiar jednego kanalu, skanowanie kanalow zostalo wylaczone ADCInit.ADC_ScanConvMode = DISABLE; //Inicjalizacja ADC ADC_Init(ADC1, &ADCInit); //Wybranie kanalu z którego bedzie odczytywane //ADC kanal 12, GPIOC2, Czestotliwosc probkowania = 1Mhz ADC_RegularChannelConfig( ADC1, ADC_Channel_12, 1, ADC_SampleTime_144Cycles); ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); //Wlaczenie DMA dla ADC1 ADC_DMACmd(ADC1, ENABLE); //Wlaczenie konwersji ADC ADC_Cmd(ADC1,ENABLE);
//Rozpoczęcie konwersji ADC_SoftwareStartConv(ADC1); } //Konfiguracja DMA void DMAInit(void) { DMA_InitTypeDef DMAInit; DMA_StructInit(&DMAInit); //Wlaczenie zegara taktujacego RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //Wlaczenie ustawien standardowych DMA_DeInit(DMA2_Stream4); DMAInit.DMA_Channel = DMA_Channel_0; //deklaracja zrodla danych, rejestr ADC1 DR DMAInit.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //Adres poczatku bloku do przeslania, DMAInit.DMA_Memory0BaseAddr = (uint32_t)&ADCValue[0]; //Kierunek transferu danych z urzadzenia do pamieci DMAInit.DMA_DIR = DMA_DIR_PeripheralToMemory; //Rozmiar bufora DMAInit.DMA_BufferSize = 1; //Wylaczenie automatycznego zwiekszania adresu ADC DMAInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Wylaczenie obslugi transferu z pamieci do pamieci DMAInit.DMA_MemoryInc = DMA_MemoryInc_Enable; //Rozmiar przesylanych danych ADC, 16b DMAInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Rozmiar przesylanych danych po stronie pamieci DMAInit.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //Tryb pracy ciagly DMAInit.DMA_Mode = DMA_Mode_Circular; //Ustawienie priorytetu na wysoki DMAInit.DMA_Priority = DMA_Priority_High; //Wylaczenie trybu fifom, automatycznie wlaczony tryb bezposredni DMAInit.DMA_FIFOMode = DMA_FIFOMode_Disable; DMAInit.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //Wyzwalanie pojedyncze DMAInit.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMAInit.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //Inicjalizacja DMA DMA_Init(DMA2_Stream4, &DMAInit); //Wlaczenie DMA DMA_Cmd(DMA2_Stream4, ENABLE); }