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?