Någon som förstår det där med DMA i STM32?
Postat: 11 augusti 2021, 15:14:32
Jag har bråkat med ett litet problem allt för länge nu. Det är DMA, något som jag har använt mig mycket utav, men av någon lustig anledning så verkar det vara olika konfigurationer av DMA beroende på vilket periferi man använder hos mikrokontrollern.
Jag använder DMA till Input Capute(IC) och DMA till Sigma Delta ADC (SDADC) som har hela 16-bit upplösning, vilket är riktigt bra.
Normalt sett så använde jag interrupt på IC och SDADC, vilket fungerade bra vid låg användning. Men så fort signalerna började pulsera och analoga ingångarna fick förändrat värde, så tog interrupt upp all CPU-tid. Så därför använder jag DMA.
SDADC:
När jag konfiguerade min SDADC så valde jag att använda "Injected Conversion", istället för "Regular Conversion". Skilllnaden är att med "Injected Conversion" så får man bestämma lite mera och koppla SDADC mot en timer, vilket jag tycker är bättre istället för att låta "Regular Conversion", vilket är att processorn själv bestämmer när SDADC ska börja samlpa. Man vet aldrig, det kanske händer. Något som har inträffat mig med "Injected Conversion".
Så här ser det ut för alla SDADC:er. Jag har totalt tre stycken.
När jag startar upp mina SDADC:er så använder jag denna C-kod.
Detta resulterar att arrayerna nedan är alltid 0. Dom har alltid 0 värden i sina element.
Dessutom när jag vill andra förstärkning och förskjutning på min SDADC så anropar jag denna kod. Koden börjar med att initialisera en struktur för att sätta funktion på SDADC:n. Variabeln sdadc har 3 olika värde: 1, 2, 3 och dessa bestämmer vilken SDADC jag vill förändra. Jag börjar alltid med att stoppa DMA, men här styper jag.
Så fort jag försöker stoppa DMA så går jag in i funktionen.
Där efter går jag in i "HAL_DMA_Abort" funktionen. Här styper jag för att hdma->State är lika med HAL_DMA_STATE_READY. Alltså betyder det att DMA var inte aktiverad redan från början? Eller vad tror ni?
IC:
Ett annat problem är detta. Jag får bara IC för htim17 att fungera. Medan htim16 verkar bara köras en gång.
Det fungerar så att i STM32 så kan man välja Mode: Circular eller Mode: Normal. Circular betyder att efter att DMA har anropats och fyllt arrayerna input_capute0 och input_capute1 så börjar den om igen. Men är den satt i Mode: Normal så anropas bara DMA 1 gång.
Det som händer är att IC för HTIM17 fungerar utmärkt. När jag pulserar med 3.3v på ingången så ser jag olika värden förändras i input_capture0, medan i input_capture1 så förändras det bara 1 gång, sedan händer det inget mer. Jag använder arrayerna för att räkna ut frekvensen. För input_capture1 så blir frevensen alltid 0.156 Hz, dvs en lägsta uppdateringsfrekvensen.
Så varför anropas inte DMA när det ska göras? Någon som kan det där med IC och SDADC för DMA inom STM32?
Jag använder DMA till Input Capute(IC) och DMA till Sigma Delta ADC (SDADC) som har hela 16-bit upplösning, vilket är riktigt bra.
Normalt sett så använde jag interrupt på IC och SDADC, vilket fungerade bra vid låg användning. Men så fort signalerna började pulsera och analoga ingångarna fick förändrat värde, så tog interrupt upp all CPU-tid. Så därför använder jag DMA.
SDADC:
När jag konfiguerade min SDADC så valde jag att använda "Injected Conversion", istället för "Regular Conversion". Skilllnaden är att med "Injected Conversion" så får man bestämma lite mera och koppla SDADC mot en timer, vilket jag tycker är bättre istället för att låta "Regular Conversion", vilket är att processorn själv bestämmer när SDADC ska börja samlpa. Man vet aldrig, det kanske händer. Något som har inträffat mig med "Injected Conversion".
Så här ser det ut för alla SDADC:er. Jag har totalt tre stycken.
- SDADC1 har Timer 13 Compare Output 1 samt DMA2 kanal 3
- SDADC2 har Timer 12 Compare Output 1 samt DMA2 kanal 4
- SDADC3 har Timer 12 Compare Output 2 samt DMA2 kanal 5
När jag startar upp mina SDADC:er så använder jag denna C-kod.
Kod: Markera allt
volatile static int16_t SDADC1_Single[9];
volatile static int16_t SDADC2_Single[3];
volatile static int16_t SDADC3_Differential[5];
static float SDADC_Single_Calibration_Gain[12] = {0};
static float SDADC_Single_Calibration_Bias[12] = {0};
static float SDADC_Differential_Calibration_Gain[5] = {0};
static float SDADC_Differential_Calibration_Bias[5] = {0};
TIM_HandleTypeDef *handler_tim12;
TIM_HandleTypeDef *handler_tim13;
SDADC_HandleTypeDef *handler_sdadc1;
SDADC_HandleTypeDef *handler_sdadc2;
SDADC_HandleTypeDef *handler_sdadc3;
void STM32_PLC_Start_Analog_Input(TIM_HandleTypeDef* htim12, TIM_HandleTypeDef* htim13, SDADC_HandleTypeDef* hsdadc1, SDADC_HandleTypeDef* hsdadc2, SDADC_HandleTypeDef* hsdadc3) {
/*
* For TIM12, TIM13 and TIM16
* Timer clock: 48 Mhz
* Prescaler: 0
* Counter: 48000 (0xbb80)
* Update frequency: 1000 Hz
*/
HAL_TIM_OC_Start(htim13, TIM_CHANNEL_1); /* TIM13 Channel 1 is trigger source for SDADC1 */
HAL_TIM_OC_Start(htim12, TIM_CHANNEL_1); /* TIM12 Channel 1 is trigger source for SDADC2 */
HAL_TIM_OC_Start(htim12, TIM_CHANNEL_2); /* TIM12 Channel 2 is trigger source for SDADC3 */
if (HAL_SDADC_CalibrationStart(hsdadc1, SDADC_CALIBRATION_SEQ_1) != HAL_OK)
Error_Handler();
if (HAL_SDADC_CalibrationStart(hsdadc2, SDADC_CALIBRATION_SEQ_1) != HAL_OK)
Error_Handler();
if (HAL_SDADC_CalibrationStart(hsdadc3, SDADC_CALIBRATION_SEQ_1) != HAL_OK)
Error_Handler();
if (HAL_SDADC_PollForCalibEvent(hsdadc1, HAL_MAX_DELAY) != HAL_OK)
Error_Handler();
if (HAL_SDADC_PollForCalibEvent(hsdadc2, HAL_MAX_DELAY) != HAL_OK)
Error_Handler();
if (HAL_SDADC_PollForCalibEvent(hsdadc3, HAL_MAX_DELAY) != HAL_OK)
Error_Handler();
if(HAL_SDADC_InjectedStart_DMA(hsdadc1, (uint32_t*)SDADC1_Single, 9) != HAL_OK)
Error_Handler();
if(HAL_SDADC_InjectedStart_DMA(hsdadc2, (uint32_t*)SDADC2_Single, 3) != HAL_OK)
Error_Handler();
if(HAL_SDADC_InjectedStart_DMA(hsdadc3, (uint32_t*)SDADC3_Differential, 5) != HAL_OK)
Error_Handler();
/* Save */
handler_tim13 = htim13;
handler_tim12 = htim12;
handler_sdadc1 = hsdadc1;
handler_sdadc2 = hsdadc2;
handler_sdadc3 = hsdadc3;
}
Kod: Markera allt
volatile static int16_t SDADC1_Single[9];
volatile static int16_t SDADC2_Single[3];
volatile static int16_t SDADC3_Differential[5];
Kod: Markera allt
/* Inline is only for optimization */
static inline int16_t* array_to_pointer(int16_t array[]){
return array; /* This return the address */
}
void STM32_PLC_Analog_Input_Set_Gain_Offset(uint8_t sdadc, uint8_t configuration_index, uint8_t gain, uint16_t offset) {
/* Initial */
SDADC_ConfParamTypeDef ConfParamStruct = {0};
ConfParamStruct.CommonMode = SDADC_COMMON_MODE_VSSA;
SDADC_HandleTypeDef *handler;
uint8_t length_DMA;
uint32_t *array_DMA;
/* Stop DMA */
switch(sdadc){
case 1:
/* Stop DMA for SDADC1 */
if(HAL_SDADC_InjectedStop_DMA(handler_sdadc1) != HAL_OK)
Error_Handler();
handler = handler_sdadc1;
length_DMA = 9;
array_DMA = (uint32_t*)array_to_pointer((int16_t*)SDADC1_Single);
ConfParamStruct.InputMode = SDADC_INPUT_MODE_SE_ZERO_REFERENCE;
break;
case 2:
/* Stop DMA for SDADC2 */
if(HAL_TIM_OC_Stop(handler_tim12, TIM_CHANNEL_1))
Error_Handler();
if(HAL_SDADC_InjectedStop_DMA(handler_sdadc2) != HAL_OK)
Error_Handler();
handler = handler_sdadc2;
length_DMA = 3;
array_DMA = (uint32_t*)array_to_pointer((int16_t*)SDADC2_Single);
ConfParamStruct.InputMode = SDADC_INPUT_MODE_SE_ZERO_REFERENCE;
break;
case 3:
/* Stop DMA for SDADC3 */
if(HAL_SDADC_InjectedStop_DMA(handler_sdadc3) != HAL_OK)
Error_Handler();
handler = handler_sdadc3;
length_DMA = 5;
array_DMA = (uint32_t*)array_to_pointer((int16_t*)SDADC3_Differential);
ConfParamStruct.InputMode = SDADC_INPUT_MODE_DIFF;
break;
default:
/* Stop DMA for SDADC1 */
if(HAL_SDADC_InjectedStop_DMA(handler_sdadc1) != HAL_OK)
Error_Handler();
handler = handler_sdadc1;
length_DMA = 9;
array_DMA = (uint32_t*)array_to_pointer((int16_t*)SDADC1_Single);
ConfParamStruct.InputMode = SDADC_INPUT_MODE_SE_ZERO_REFERENCE;
break;
}
/* Set gain and offset */
switch(gain){
case 0:
ConfParamStruct.Gain = SDADC_GAIN_1_2;
break;
case 1:
ConfParamStruct.Gain = SDADC_GAIN_1;
break;
case 2:
ConfParamStruct.Gain = SDADC_GAIN_2;
break;
case 3:
ConfParamStruct.Gain = SDADC_GAIN_4;
break;
case 4:
ConfParamStruct.Gain = SDADC_GAIN_8;
break;
case 5:
ConfParamStruct.Gain = SDADC_GAIN_16;
break;
case 6:
ConfParamStruct.Gain = SDADC_GAIN_32;
break;
default:
ConfParamStruct.Gain = SDADC_GAIN_1;
offset = 0;
break;
}
ConfParamStruct.Offset = offset;
/* Set calibration */
if (HAL_SDADC_PrepareChannelConfig(handler, configuration_index, &ConfParamStruct) != HAL_OK)
Error_Handler();
/* Start ADC again */
if (HAL_SDADC_CalibrationStart(handler, SDADC_CALIBRATION_SEQ_1) != HAL_OK)
Error_Handler();
if (HAL_SDADC_PollForCalibEvent(handler, HAL_MAX_DELAY) != HAL_OK)
Error_Handler();
if(HAL_SDADC_InjectedStart_DMA(handler, array_DMA, length_DMA) != HAL_OK)
Error_Handler();
}
Kod: Markera allt
HAL_StatusTypeDef HAL_SDADC_InjectedStop_DMA(SDADC_HandleTypeDef *hsdadc)
{
HAL_StatusTypeDef status;
/* Check parameters */
assert_param(IS_SDADC_ALL_INSTANCE(hsdadc->Instance));
/* Check SDADC state */
if((hsdadc->State != HAL_SDADC_STATE_INJ) && \
(hsdadc->State != HAL_SDADC_STATE_REG_INJ))
{
/* Return error status */
status = HAL_ERROR;
}
else
{
/* Clear JDMAEN bit in SDADC_CR1 register */
hsdadc->Instance->CR1 &= ~(SDADC_CR1_JDMAEN);
/* Stop current DMA transfer */
if(HAL_DMA_Abort(hsdadc->hdma) != HAL_OK)
{
/* Set SDADC in error state */
hsdadc->State = HAL_SDADC_STATE_ERROR;
status = HAL_ERROR;
}
Kod: Markera allt
HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma)
{
if(hdma->State != HAL_DMA_STATE_BUSY)
{
/* no transfer ongoing */
hdma->ErrorCode = HAL_DMA_ERROR_NO_XFER;
/* Process Unlocked */
__HAL_UNLOCK(hdma);
return HAL_ERROR;
}
Ett annat problem är detta. Jag får bara IC för htim17 att fungera. Medan htim16 verkar bara köras en gång.
Kod: Markera allt
volatile static uint16_t input_capture0[2] = {0};
volatile static uint16_t input_capture1[2] = {0};
void STM32_PLC_Start_Input_Capture(TIM_HandleTypeDef* htim17, TIM_HandleTypeDef* htim16) {
/*
* Input capture for measuring frequency
* For TIM17 and TIM16
* Timer clock: 48 Mhz
* Prescaler: 4799
* Counter: 65535 (0xffff)
* Update frequency: 0.1526 Hz (1/0.1526 = 6.5535 seconds)
* Example: For every second, it will count 10000
* Lowest frequency measurement: 1/(0xFFFF*0.0001) = 0.1526 Hz
* Highest frequency measurement: 1/(1*0.0001) = 10000 Hz
*/
if(HAL_TIM_IC_Start_DMA(htim16, TIM_CHANNEL_1, (uint32_t*)input_capture1, 2) != HAL_OK)
Error_Handler();
if(HAL_TIM_IC_Start_DMA(htim17, TIM_CHANNEL_1, (uint32_t*)input_capture0, 2) != HAL_OK)
Error_Handler();
}
Så varför anropas inte DMA när det ska göras? Någon som kan det där med IC och SDADC för DMA inom STM32?