środa, 6 stycznia 2016

[6] STM32F4 - Discovery - ADC, DMA, Wyświetlacz HD44780

Tematem tego postu będzie połączenie układów ADC, DMA z wyświetlaczem wyposażonym w sterownik HD44780.

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);
}