Na rozgrzewkę:
W poprzednim wpisie z tej serii wspomniałem o użyciu timera do wyzwalania pomiaru ADC. Teraz chcę Ci zaprezentować implementację tego rozwiązania oraz jego cel, więc do rzeczy.
Zakres wpisu:
- Mierzone wartości ADC w projekcie
- Synchronizacja PWM z pomiarem
- Moment pomiaru
- DMA (Direct Memory Access)
Mierzone wartości ADC w projekcie:
Pomiary ADC są wykorzystywane w projekcie do odczytu wartości prądu oraz napięcia na danym wyjściu. Odczyt prądu wykonywany jest metodą low-side, który wybrałem z powodu tańszej implementacji niż high-side. Pomiar low-side nie zabezpiecza przed zewnętrznym zwarciem wyjścia do masy. Dlatego niezbędne jest zastosowanie jakiegoś dodatkowego rozwiązania, aby zwarcie nie spowodowało spalenia urządzenia. Do rozwiązania wspomnianego problemu wybrałem pomiar napięcia każdego wyjścia względem masy. Jeśli wartość napięcia będzie niższa niż ustawiony poziom przy jednocześnie włączonym wyjściu to nastąpi detekcja zwarcia i wyjście zostanie wyłączone. Jak wspomniałem poprzednio w projekcie będzie około 30 wyjść, dlatego niezbędne będą multipleksery.
Synchronizacja PWM z pomiarem:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseInitTypeDef tim2; tim2.TIM_CounterMode = TIM_CounterMode_Up; tim2.TIM_Prescaler = 10000; tim2.TIM_Period = 41; tim2.TIM_ClockDivision = TIM_CKD_DIV1; tim2.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &tim2); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);Poniżej zamieszczam część konfiguracji przetwornika ADC, do użycia wyzwalania pomiaru za pomocą timera:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); ADC_InitTypeDef adc; ADC_DeInit(); adc.ADC_DataAlign = ADC_DataAlign_Right; adc.ADC_Resolution = ADC_Resolution_12b; adc.ADC_ContinuousConvMode = DISABLE; adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising adc.ADC_NbrOfConversion = 4; adc.ADC_ScanConvMode = ENABLE; ADC_Init(ADC1, &adc);Pomiar ADC można wyzwalać używając także innych timerów, poniżej przedstawiam tabelę z Reference Manual [1], w której zawarte są informację na temat innych zdarzeń mogących wyzwalać pomiar.
Moment pomiaru:
Jak wspomniałem powyżej, pomiar ADC jest zsynchronizowany z przebiegiem PWM, tak aby był realizowany tylko i wyłącznie podczas stanu wysokiego. W momencie generowania stanu wysokiego wyzwalany jest timer związany z pomiarem ADC, zaś w stanie niskim timer jest dezaktywowany.
W programie zawarłem zabezpieczenie od chwilowej niesynchronicznej pracy PWM (np. podczas małego wypełnienia), które ma wpływ na pomiar ADC. Zabezpieczenie jest zrealizowane w postaci programowego filtru dolnoprzepustowego, pojedyncze niezsynchronizowane próbki nie będą wpływały na wynik pomiaru.
DMA (Direct Memory Access):
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_InitTypeDef DMAInit; DMA_DeInit(DMA2_Stream0); DMAInit.DMA_BufferSize = 4; DMAInit.DMA_Channel = DMA_Channel_0; DMAInit.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMAInit.DMA_Memory0BaseAddr = (uint32_t)&ADCValue[0]; DMAInit.DMA_DIR = DMA_DIR_PeripheralToMemory; DMAInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMAInit.DMA_MemoryInc = DMA_MemoryInc_Enable; DMAInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMAInit.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMAInit.DMA_Mode = DMA_Mode_Circular; DMAInit.DMA_Priority = DMA_Priority_High; DMAInit.DMA_FIFOMode = DMA_FIFOMode_Disable; DMAInit.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMAInit.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMAInit.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMAInit); DMA_StructInit(&DMAInit); DMA_Cmd(DMA2_Stream0, ENABLE);
Informację o tym, którego użyć strumienia DMA, oraz kanału także znajdziesz w Reference Manual [1]:
Poniżej kilka słów komentarza do konfiguracji DMA:
- Pisząc program testy wykonywałem na Discovery używając diod umieszczonych na płytce, dlatego rozmiar bufora wynosi 4.
- Wartość ADC przechowywana jest w rejestrze DR.
- Wartość ADC kopiowana jest do zmiennej ADCValue[0]. W konfiguracji wskazujemy tylko pierwszy adres zmiennej.
- Inkrementacja peryferiów jest wyłączona z racji odczytu z jednego rejestru.
- Inkrementacja pamięci jest załączona dlatego, że wykonywany jest pomiar w tym momencie dla 4 wartości
- FIFO mode jest wyłączony, czyli załączony jest tryb bezpośredni. Mogłem sobie pozwolić na taką konfigurację z racji tego samego rozmiaru transferowanych danych w peryferiach jak i w pamięci. Tryb FIFO jest wykorzystywany, gdy rozmiary te różnią się.
- FIFO threshold, wskazuję wartość po jakiej ma się odbyć transfer z peryferiów do pamięci, lecz jest wykorzystywany tylko w momencie aktywnej funkcji FIFO.
- Tryb burst jest także tylko aktywny, kiedy jest włączona funkcja FIFO.
[1] Dostęp w Internecie: https://www.st.com/resource/en/reference_manual/dm00096844-stm32f401xb-c-and-stm32f401xd-e-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf