Trying to make simple PWM transmitter i faced with a problem. I have TIM2 with Channel2 (in PWM Generation mode) on board NUCLEO F042K6 and USART1 connected to the board in Async mode (USART works in DMA).
I wanted to make a PWM transmitter that uses a circular buffer that can be filled with one character or several characters at once. The plan was that when the user enters a character (or several characters), the idle line callback is processed, in which a function is called that stores the data in a cyclic buffer. After saving, the data transferred to the buffer is passed to the function PWM_SendChar(...) to convert the data into bits and transfer them over the PWM line. But the problem is when the program goes into PWM_SendChar(...) the callback HAL_TIM_PWM_PulseFinishedCallback(...) isn't called and program stucks in infinite while loop because variable used as a flag for detecting the end of single pulse (PWM_data_sent_flag) wasn't set to value 1.
However, if I use HAL_UART_Recieve(...) func in while(1) loop (located in main(void)) to recieve a char from user program works fine without stucking in while loop in TIM callback. Ofc, for this method I don't use HAL_UARTEx_RxEventCallback(...) and cycle buffer.
Thats's why I think the problem is in USART idle line detection and please help me solve it.
Code Example
Global variable used for waiting the end of pulse:
volatile uint16_t PWM_data_sent_flag = 0;
PWM timer callback:
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
PWM_data_sent_flag = 1;
}
}
Callback for USART idle line:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart1) {
// Function_to_save_input_char_in_cycle_buffer() is called
}
}
Function used to send char as a PWM signal (emited after Function_to_save_input_char_in_cycle_buffer() completed):
void PWM_SendChar(char ch) {
for (int i = 0, mv = 7; i <= 7; ++i, --mv) {
uint8_t bit = (ch & (1 << mv)) ? 1 : 0;
// PWM_LOW_PERCENT encodes bit with zero value, PWM_HIGH_PERCENT encodes bit with value one
uint16_t duty_cycle = bit == 0 ? PWM_LOW_PERCENT : PWM_HIGH_PERCENT;
// Change TIM2 CCR2 value according to bit's value
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2,
GET_PERCENT_VALUE(TIM2->ARR, duty_cycle));
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_2);
// Program comes to this cycle and doesn't leave it
while (!PWM_data_sent_flag)
;
PWM_data_sent_flag = 0;
}
}
USART receive to idle launch (half data transmitted callback disabled):
int main(void) {
// Init code
// ...
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t*) ring_buf.buff,
ARRAY_LEN(ring_buf.buff)) != HAL_OK) {
Error_Handler();
}
// Disable half word transmitted callback
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);
while(1) {
}
}
TIM2 settings:
static void MX_TIM2_Init(void) {
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
TIM_OC_InitTypeDef sConfigOC = { 0 };
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 48000 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 100 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig)
!= HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2)
!= HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
USART1 settings:
static void MX_USART1_UART_Init(void) {
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
DMA settings:
static void MX_DMA_Init(void) {
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel4_5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
}
Related
I need to get interrupt at compare match of PWM.
I am using STM32CubeIDE 1.6.1, STM32Cube FW_F1 V1.8.3
even tried LL library instead of HAL inside \STM32Cube\Repository\STM32Cube_FW_F1_V1.8.3\Projects\STM32F103RB-Nucleo\Examples_LL\TIM directory but no luck!
I commented everything I tried before but didn't get the result I wanted.
I uploaded the logic analyzer result at the end.
thank you for your help.
Logic analyzer
in tim.c:
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 6400;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 100;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* USER CODE END TIM4_Init 2 */
HAL_TIM_MspPostInit(&htim4);
}
in main.c:
/* USER CODE BEGIN 2 */
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 25);
//HAL_TIM_Base_Start_IT(&htim4);
HAL_TIM_PWM_Start_IT(&htim4, TIM_CHANNEL_4);
/* USER CODE END 2 */
.
.
.
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
}
void HAL_TIM_PeriodElapsedHalfCpltCallback(TIM_HandleTypeDef *htim)
{
//HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
}
void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim)
{
//HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
}
/* USER CODE END 4 */
in stm32f1xx_it.c:
void TIM4_IRQHandler(void)
{
/* USER CODE BEGIN TIM4_IRQn 0 */
//HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
// /* Check whether CC4 interrupt is pending */
// if (LL_TIM_IsActiveFlag_CC4(TIM4) == 1)
// {
// /* Clear the update interrupt flag*/
// LL_TIM_ClearFlag_CC4(TIM4);
//
// /* TIM4 capture/compare interrupt processing */
// if (LL_TIM_GetCounter(TIM4) == LL_TIM_OC_GetCompareCH4(TIM4))
// {
//
// //HAL_GPIO_TogglePin(TEST_GPIO_Port, TEST_Pin);
// }
// }
/* USER CODE END TIM4_IRQn 0 */
HAL_TIM_IRQHandler(&htim4);
/* USER CODE BEGIN TIM4_IRQn 1 */
/* USER CODE END TIM4_IRQn 1 */
}
TIM4 Config
TIM4 Config
Logic analyzer
I'm using DMA to Access the Data from my ADC. The value at the ADC changes permantenly.
I read I can use DMA so I can use the value of the ADC everytime and everywhere I want to.
Problem is that my Main while() Loop is not or just once execute. The DMA Interupt calls.
HAL_ADC_Start_DMA(&hadc, (uint32_t*) &buffer, 1);
Here is the Code for Start the DMA for the ADC. Mode is Circular.
Here is the Init:
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14
|RCC_OSCILLATORTYPE_HSI48;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
|RCC_PERIPHCLK_I2C1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief ADC Initialization Function
* #param None
* #retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
The ADC reads a analog volage from I/O.
My while(1) Loop currently just contains blinking led code.
Check the following:
Inside Hal_MSP file use DMA_CIRCULAR
create the buffer like this -> __IO uint16_t buffer[1]
then use it like this -> HAL_ADC_Start_DMA(&hadc, (uint32_t*) &buffer, 1);
Its better to start DMA at the end of ADC init. You can place above line inside the USER CODE BEGIN ADC_Init 2 comment braces.
The ADC size is 12 bit of this controller so circular DMA will automatically overwrite after every conversion.
I'm trying to convert 3 ADC channels using DMA. But the variables don't seem to change when I watch them in the debugger. I know the conversion complete callback is executed because I breakpointed it. So this suggests that the DMA transfer is not executing and the buffer is not being filled. I'm using stm32cube to initialize my project. I've trimmed the generated code. Thanks.
ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;
uint32_t uwADC8ConvertedValue = 0;
uint32_t uwADC10ConvertedValue = 0;
uint32_t uwADC11ConvertedValue = 0;
uint32_t adcBuffer[3];
/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *AdcHandle) {
uwADC8ConvertedValue = adcBuffer[0];
uwADC10ConvertedValue = adcBuffer[1];
uwADC11ConvertedValue = adcBuffer[2];
}
/* USER CODE END 0 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_RTC_Init();
MX_ADC_Init();
MX_USART2_UART_Init();
MX_USART5_UART_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_ADC_Start_IT(&hadc);
HAL_ADC_Start_DMA(&hadc, adcBuffer, 3);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_UART_Transmit(&huart2, TxBuffer, 15, 5000);
GPIO_PinState userPBstate = OFF;
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(MODEM_PW_GPIO_Port, MODEM_PW_Pin);
HAL_GPIO_TogglePin(CAM1_LD_GPIO_Port, CAM1_LD_Pin);
HAL_GPIO_TogglePin(CAM1_PW_GPIO_Port, CAM1_PW_Pin);
HAL_Delay(100);
userPBstate = HAL_GPIO_ReadPin(USER_PB_GPIO_Port, USER_PB_Pin);
if (userPBstate == ON) {
HAL_UART_Transmit(&huart2, (uint8_t *)"User button pressed!\n\r", 22, 5000);
}
else {
HAL_UART_Transmit(&huart2, (uint8_t *)"User button NOT pressed!\n\r", 26, 5000);
}
}
/* USER CODE END 3 */
}
/* ADC init function */
static void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_10;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_11;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
User custom code:
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_ADC_Start_IT(&hadc);
HAL_ADC_Start_DMA(&hadc, adcBuffer, 3);
/* USER CODE END 2 */
should be:
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_ADC_Start_DMA(&hadc, adcBuffer, 3);
/* USER CODE END 2 */
If you want to use DMA mode, then do not use interrupt mode.
I'm trying to start a STM32L576VGT with FreeRTOS.
I have a led blinking from a task and now I'm trying to set a PWM on, freq and duty is not important now, is a "hello world pwm".
All is done using CubeMx, and I'm been unable to make it work.
tim.c generated with CubeMx for TIM15
/* TIM15 init function */
void MX_TIM15_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
htim15.Instance = TIM15;
htim15.Init.Prescaler = 47999;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 200;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim15) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim15, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim15, &sBreakDeadTimeConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim15);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
{
if(tim_pwmHandle->Instance==TIM15)
{
/* USER CODE BEGIN TIM15_MspInit 0 */
/* USER CODE END TIM15_MspInit 0 */
/* TIM15 clock enable */
__HAL_RCC_TIM15_CLK_ENABLE();
/* USER CODE BEGIN TIM15_MspInit 1 */
/* USER CODE END TIM15_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(timHandle->Instance==TIM15)
{
/* USER CODE BEGIN TIM15_MspPostInit 0 */
/* USER CODE END TIM15_MspPostInit 0 */
/**TIM15 GPIO Configuration
PB15 ------> TIM15_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF14_TIM15;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM15_MspPostInit 1 */
/* USER CODE END TIM15_MspPostInit 1 */
}
}
main in main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_QUADSPI_Init();
MX_LPUART1_UART_Init();
MX_SPI2_Init();
MX_I2C1_Init();
MX_TIM15_Init();
/* USER CODE BEGIN 2 */
// DISP_diplay_init();
HAL_TIM_PWM_Start(&htim15,TIM_CHANNEL_2);
/* USER CODE END 2 */
/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
I'm using TIM15 because the PCB is already done and it is the one is connected to the output i want to control.
Thanks,
Guillermo
Have you tried with different values on sConfigOC.Pulse = 0;?
STM32F072CBU microcontroller.
I have multiple inputs to the ADC and would like to read them individually and separately. STMcubeMX produces boilerplate code which assumes I wish to read all of the inputs sequentially, and I have not been able to figure out how to correct this.
This blog post expresses the same problem I am having, but the solution given doesn't seem to work. Turning the ADC on and off for each conversion correlates with error in the returned value. Only when I configure a single ADC input in STMcubeMX and then poll without de-initializing the ADC are accurate readings returned.
cubeMX's adc_init function:
/* ADC init function */
static void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_2;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_3;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_4;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//HAL_TIM_Base_Start_IT(&htim3);
init_printf(NULL, putc_wrangler);
HAL_ADCEx_Calibration_Start(&hadc);
HAL_ADC_DeInit(&hadc); // ADC is initialized for every channel change
schedule_initial_events();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
event_loop();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
My process right now for turning the ADC off and reinitializing to change channels:
// Set up
ADC_ChannelConfTypeDef channelConfig;
channelConfig.SamplingTime = samplingT;
channelConfig.Channel = sensorChannel;
channelConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_ADC_ConfigChannel(&hadc, &channelConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
// Convert
uint16_t retval;
if (HAL_ADC_Start(&hadc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_ADC_PollForConversion(&hadc, 1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_ADC_GetError(&hadc) != HAL_ADC_ERROR_NONE)
{
_Error_Handler(__FILE__, __LINE__);
}
retval = (uint16_t) HAL_ADC_GetValue(&hadc);
if (HAL_ADC_Stop(&hadc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
// Close
HAL_ADC_DeInit(&hadc);
At this point I'm not really sure that there's a way to accomplish what I want, STM32 seems dead set on active ADC lines being in the regular group and being converted in order.
If you want to read several ADC channels in single conversion mode then you have to change the channel setting before each reading, but you do not have to reinit the ADC. Simply do as below, select the new channel (you can change sampling time too if it must be different for the channels but generally it can be the same), select the channel rank and then call the HAL_ADC_ConfigChannel function. After this you can perform a conversion.
void config_ext_channel_ADC(uint32_t channel, boolean_t val)
{
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = channel;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
if(True == val)
{
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
}
else
{
sConfig.Rank = ADC_RANK_NONE;
}
HAL_ADC_ConfigChannel(&hadc, &sConfig);
}
uint32_t r_single_ext_channel_ADC(uint32_t channel)
{
uint32_t digital_result;
config_ext_channel_ADC(channel, True);
HAL_ADCEx_Calibration_Start(&hadc);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1000);
digital_result = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
config_ext_channel_ADC(channel, False);
return digital_result;
}
An example for usage:
#define SUPPLY_CURRENT ADC_CHANNEL_5
#define BATTERY_VOLTAGE ADC_CHANNEL_6
uint16_t r_battery_voltage(uint16_t mcu_vcc)
{
float vbat;
uint16_t digital_val;
digital_val = r_single_ext_channel_ADC(BATTERY_VOLTAGE);
vbat = (mcu_vcc/4095.0) * digital_val;
vbat = vbat * 2; // 1/2 voltage divider
return vbat;
}
uint16_t r_supply_current(uint16_t mcu_vcc)
{
float v_sense, current;
uint16_t digital_val;
digital_val = r_single_ext_channel_ADC(SUPPLY_CURRENT);
v_sense = (mcu_vcc/4095.0) * digital_val;
current = v_sense * I_SENSE_GAIN;
return current;
}
This code was used on an STM32F030. For reading the internal temperature sensor and reference voltage a slightly different version of the above seen functions needed as additional enable bits must be set.
void config_int_channel_ADC(uint32_t channel, boolean_t val)
{
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = channel;
if(val == True)
{
if(channel == ADC_CHANNEL_VREFINT)
{
ADC->CCR |= ADC_CCR_VREFEN;
hadc.Instance->CHSELR = (uint32_t)(ADC_CHSELR_CHSEL17);
}
else if(channel == ADC_CHANNEL_TEMPSENSOR)
{
ADC->CCR |= ADC_CCR_TSEN;
hadc.Instance->CHSELR = (uint32_t)(ADC_CHSELR_CHSEL16);
}
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
}
else if(val == False)
{
if(channel == ADC_CHANNEL_VREFINT)
{
ADC->CCR &= ~ADC_CCR_VREFEN;
hadc.Instance->CHSELR = 0;
}
else if(channel == ADC_CHANNEL_TEMPSENSOR)
{
ADC->CCR &= ~ADC_CCR_TSEN;
hadc.Instance->CHSELR = 0;
}
sConfig.Rank = ADC_RANK_NONE;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
}
HAL_ADC_ConfigChannel(&hadc,&sConfig);
}
uint32_t r_single_int_channel_ADC(uint32_t channel)
{
uint32_t digital_result;
config_int_channel_ADC(channel, True);
HAL_ADCEx_Calibration_Start(&hadc);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1000);
digital_result = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
config_int_channel_ADC(channel, False);
return digital_result;
}
Example usage internal voltage reference for MCU VDD calculation:
#define VREFINT_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7BA))
static float FACTORY_CALIB_VDD = 3.31;
uint16_t calculate_MCU_vcc()
{
float analog_Vdd;
uint16_t val_Vref_int = r_single_int_channel_ADC(ADC_CHANNEL_VREFINT);
analog_Vdd = (FACTORY_CALIB_VDD * (*VREFINT_CAL_ADDR))/val_Vref_int;
return analog_Vdd * 1000;
}
Internal temperature sensor reading:
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
static float FACTORY_CALIB_VDD = 3.31;
float r_MCU_temp(uint16_t mcu_vcc)
{
float temp;
float slope = ((110.0 - 30.0)/((*TEMP110_CAL_ADDR) - (*TEMP30_CAL_ADDR)));
uint16_t ts_data = r_single_int_channel_ADC(ADC_CHANNEL_TEMPSENSOR);
temp = ((mcu_vcc/FACTORY_CALIB_VDD) * ts_data)/1000;
temp = slope * (temp - (*TEMP30_CAL_ADDR)) + 30;
return round_to(temp, 0);
}
Note that calibration data addresses might be different for your MCU, check the datasheet for more information.
I had the similar problem. I was using STM32F091RC. On ADC_V_PIN I had external multiplexer. On ADC_T_PIN I had NTC reading. I was controlling external multiplexer with GPIOs (this is not seen in the code below). What I needed was multiple successive readings of ADC_V_PIN, after that single ADC_T_PIN reading. With default sequential reading of ADC I would get between each external multiplexed reading of ADC_V_PIN also ADC_T_PIN reading. Using HAL_ADC_ConfigChannel multiple times seemed to OR with each other, and I had problems with reading. So I didn't use HAL_ADC_ConfigChannel at all. Instead, I reconfigured CHSELR register before each software triggered AD conversion. This was the way to make internal and external multiplexer work together.
Here is initialization code:
GPIO_InitStruct.Pin = ADC_V_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ADC_V_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = ADC_T_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ADC_T_PORT, &GPIO_InitStruct);
g_AdcHandle.Instance = ADC1;
if (HAL_ADC_DeInit(&g_AdcHandle) != HAL_OK)
{
/* ADC initialization error */
Error_Handler();
}
g_AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
g_AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
g_AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
g_AdcHandle.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;;
g_AdcHandle.Init.ContinuousConvMode = DISABLE;
g_AdcHandle.Init.DiscontinuousConvMode = ENABLE;
g_AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
g_AdcHandle.Init.LowPowerAutoWait = DISABLE;
g_AdcHandle.Init.LowPowerAutoPowerOff = DISABLE;
g_AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
g_AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
g_AdcHandle.Init.DMAContinuousRequests = DISABLE;
g_AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
g_AdcHandle.Init.SamplingTimeCommon = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_Init(&g_AdcHandle) != HAL_OK)
{
/* ADC initialization error */
Error_Handler();
}
if (HAL_ADCEx_Calibration_Start(&g_AdcHandle) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
while(1){
ADC1->CHSELR = ADC_CHSELR_CHSEL0;
HAL_ADC_Start(&g_AdcHandle);
HAL_ADC_PollForConversion(&g_AdcHandle, 10);
V = HAL_ADC_GetValue(&g_AdcHandle);
HAL_ADC_Stop(&g_AdcHandle);
ADC1->CHSELR = ADC_CHSELR_CHSEL10;
HAL_ADC_Start(&g_AdcHandle);
HAL_ADC_PollForConversion(&g_AdcHandle, 10);
T = HAL_ADC_GetValue(&g_AdcHandle);
HAL_ADC_Stop(&g_AdcHandle);
}