【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波
[复制链接]
本帖最后由 donatello1996 于 2020-8-10 12:01 编辑
串口空闲中断和PWM方波输出都是实际产品项目中非常常用的功能,串口空闲中断的好处在于可以使用不轮询方式,不占用CPU资源的前提下进行不定长串口字符串接收,因为触发方式是使用中断。使用空闲中断实现不定长接收的方式非常简单,只需要两点,一个是开启空闲中断,一个是开启接收DMA,这边我使用开发板的Arduino接口上面的串口6(USART6)来进行:
接收DMA初始化使用CubeMX自动一键生成:
UART_HandleTypeDef huart6;
DMA_HandleTypeDef hdma_usart6_rx;
void UART6_Init(int baud)
{
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_USART6_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
huart6.Instance = USART6;
huart6.Init.BaudRate = baud;
huart6.Init.WordLength = UART_WORDLENGTH_8B;
huart6.Init.StopBits = UART_STOPBITS_1;
huart6.Init.Parity = UART_PARITY_NONE;
huart6.Init.Mode = UART_MODE_TX_RX;
huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart6.Init.OverSampling = UART_OVERSAMPLING_16;
huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
HAL_UART_Init(&huart6);
hdma_usart6_rx.Instance = DMA2_Stream1;
hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5;
hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart6_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart6_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_usart6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_usart6_rx);
__HAL_LINKDMA(&huart6,hdmarx,hdma_usart6_rx);
__HAL_UART_ENABLE_IT(&huart6,UART_IT_IDLE);
HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART6_IRQn);
}
void USART6_IRQHandler()
{
int temp;
if(__HAL_UART_GET_FLAG(&huart6, UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart6);
HAL_UART_DMAStop(&huart6);
temp=__HAL_DMA_GET_COUNTER(&hdma_usart6_rx);
rx6_len=BUFFERSIZE-temp;
uart6_recv_end_flag=1;
}
}
主循环中使用轮询处理代码处理空闲中断中接收到的数据,这里是只有触发了空闲中断才会处理数据并重新开启DMA,所以是不会占用CPU轮询资源的:
#define BUFFERSIZE 255
unsigned char rx6_buf[BUFFERSIZE],rx6_len=0,uart6_recv_end_flag;
void UART6_DMA_Get()
{
if(uart6_recv_end_flag==1)
{
uart6_recv_end_flag=0;
printf("%s %d\n",rx6_buf,rx6_len);
HAL_UART_Receive_DMA(&huart6, (unsigned char*)rx6_buf, BUFFERSIZE);
}
}
将串口6接入一个定时打印数值的传感器,就可以采集数据了,不过值得一提的是,串口空闲中断本质上仍是中断,如果空闲中断触发次数过于频繁,会严重影响CPU正常轮询工作,所以选用的串口传感器不能是每一帧发送间隔太短,频繁触发空闲中断的传感器。
然后是PWM,这里我使用Arduino接口的PF6即TIM10的通道1,可以直接参考原子代码来修改进行初始化:
TIM_HandleTypeDef TIM10_Handler;
TIM_OC_InitTypeDef TIM10_CH1Handler;
void TIM10_PWM_Init(int arr,int psc)
{
__HAL_RCC_TIM10_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
GPIO_InitTypeDef GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_6;
GPIO_Initure.Mode=GPIO_MODE_AF_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
GPIO_Initure.Alternate=GPIO_AF3_TIM10;
HAL_GPIO_Init(GPIOF,&GPIO_Initure);
TIM10_Handler.Instance=TIM10;
TIM10_Handler.Init.Prescaler=psc;
TIM10_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;
TIM10_Handler.Init.Period=arr;
TIM10_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM10_Handler);
TIM10_CH1Handler.OCMode=TIM_OCMODE_PWM1;
//模式选择PWM1
TIM10_CH1Handler.Pulse=arr/2;
//设置比较值,此值用来确定占空比,
//默认比较值为自动重装载值的一半,即占空比为50%
TIM10_CH1Handler.OCPolarity=TIM_OCPOLARITY_LOW;
HAL_TIM_PWM_ConfigChannel(&TIM10_Handler,&TIM10_CH1Handler,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&TIM10_Handler,TIM_CHANNEL_1);
}
void TIM_SetTIM10Compare1(int compare)
{
TIM10->CCR1=compare;
}
输出效果:
这里可以提一下TIM10的两个初始化参数,一个是重装载值,一个是分频值,重装载值的作用是设置一个PWM比较阈值,比如256,在0~256范围内占空比是0%~100%变化,而分频值就是PWM输出方波的频率,分频值越小,PWM频率越高,输出一个方波周期的时间就越短,这里我设置TIM10分频值为1,PWM波的输出间隔达到了1us级别,即1MHz,不过这样仍然算是频率较低的,之后我再继续探讨看看怎么输出更高频率的方波。
其它参数中,向上计数模式即为增量模式,即TIM10的计数器越计越多,模式为PWM1即占空比数值越大,一个周期内的低电平时间越长。
|