CW32L083的UART是没有空闲中断的,这样给接收数据帧带来一定的不便。比如我们需要用的AT指令,那么判断接收完一条完整的指令,可以用串口断+结尾rn来判断接收完一条完整指令。但是这个方法有一个缺点就是返回两个rn 时就会处理起来非常麻烦。
这是我用串口中断+定时器来实现 如空闲中断的接收,而且可以灵活的处理接收最后一个数据后的延时来判是否接收完整一条指令。
【实现方法】
1、初始化串口5,开配置波特率为115200,开启接收中断。
static void SerialInit(uint32_t BaudRate)
{
uint32_t PCLK_Freq;
GPIO_InitTypeDef GPIO_InitStructure = {0};
UART_InitTypeDef UART_InitStructure = {0};
PCLK_Freq = SystemCoreClock > > pow2_table[CW_SYSCTRL- >CR0_f.HCLKPRS];
PCLK_Freq > >= pow2_table[CW_SYSCTRL- >CR0_f.PCLKPRS];
// 调试串口使用UART5
// PB8- >TX
// PB9< -RX
// 时钟使能
__RCC_GPIOB_CLK_ENABLE();
__RCC_UART5_CLK_ENABLE();
// 先设置UART TX RX 复用,后设置GPIO的属性,避免口线上出现毛刺
PB08_AFx_UART5TXD();
PB09_AFx_UART5RXD();
GPIO_InitStructure.Pins = GPIO_PIN_8;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.Pins = GPIO_PIN_9;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
UART_InitStructure.UART_BaudRate = BaudRate;
UART_InitStructure.UART_Over = UART_Over_16;
UART_InitStructure.UART_Source = UART_Source_PCLK;
UART_InitStructure.UART_UclkFreq = PCLK_Freq;
UART_InitStructure.UART_StartBit = UART_StartBit_FE;
UART_InitStructure.UART_StopBits = UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(CW_UART5, &UART_InitStructure);
//优先级,无优先级分组
NVIC_SetPriority(UART2_UART5_IRQn, 0);
//UARTx中断使能
NVIC_EnableIRQ(UART2_UART5_IRQn);
}
2、定义一个结构体来储存串口的数据:
typedef struct _uartx_infor{
uint8_t rx_cnt;
uint8_t tx_cnt;
uint8_t rx_buff[UART_LEN_MAX];
uint8_t tx_buff[UART_LEN_MAX];
FunctionalState rx_state; //接收状态
} uartx_infor;
3、初始化GTIM1定时器,定义为一次计时,主频为64M,所以配置16分频 预载值为40000,从而实现10毫秒溢出产生中断。
/* 初始化GTIM定时1 创建10ms的中断*/
void init_gtim1(void)
{
GTIM_InitTypeDef GTIM_InitStruct = {0};
__RCC_GTIM1_CLK_ENABLE(); // GTIM1时钟使能
__disable_irq();
NVIC_EnableIRQ(GTIM1_IRQn);
__enable_irq();
GTIM_InitStruct.Mode = GTIM_MODE_TIME;
GTIM_InitStruct.OneShotMode = GTIM_COUNT_ONESHOT; //只运行一次
GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV16;
GTIM_InitStruct.ReloadValue = 40000UL - 1; // PWM频率为 64M/16/ 4M =100Hz
GTIM_InitStruct.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
//GTIM_Cmd(CW_GTIM1, ENABLE);
}
4、定义串口中断函数如下,主要的思想就是触发接收中断后,停止定时器的运行,重装ARR值,再开启定时器,同时把接收到的数据放入缓冲区。如果接收大于最大缓冲区,则接收值归零。
/**
* @brief This funcation handles UART2
*/
void UART2_UART5_IRQHandler(void)
{
/* USER CODE BEGIN */
uint8_t rx_data;
if(UART_GetITStatus(CW_UART5, UART_IT_RC) != RESET)
{
uart5_infor.rx_state = DISABLE;
rx_data = UART_ReceiveData_8bit(CW_UART5);
if(uart5_infor.rx_cnt < UART_LEN_MAX)
{
uart5_infor.rx_buff[uart5_infor.rx_cnt] = rx_data;
CW_GTIM1- >CR0_f.EN = 0;
CW_GTIM1- >ARR = 40000-1;
CW_GTIM1- >CR0_f.EN = 1;
}
else
{
uart5_infor.rx_cnt = 0;
memset(uart5_infor.rx_buff, 0, UART_LEN_MAX);
}
uart5_infor.rx_cnt ++;
UART_ClearITPendingBit(CW_UART5, UART_IT_RC);
}
/* USER CODE END */
}
5、定时器函数为,如果触发中断,清除中断标志,把接收完整数据值置为真:
/**
* @brief This funcation handles GTIM1
*/
void GTIM1_IRQHandler(void)
{
/* USER CODE BEGIN */
if(GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV))
{
GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
uart5_infor.rx_state = ENABLE;
}
/* USER CODE END */
}
6、在主函数中,我们判断接收状态是否为真,如果为真则打印出接收的数值,并且重置串口数据。
while (1)
{
if(uart5_infor.rx_state == ENABLE)
{
printf("uart5 recv cnt:%drn", uart5_infor.rx_cnt);
printf("uart5 recv cnt:%srn", uart5_infor.rx_buff);
uart5_infor_init();
}
// GPIO_TogglePin(CW_GPIOC, GPIO_PIN_2);
// rt_thread_mdelay(500);
rt_thread_mdelay(10);
}
【实验效果】
我们发送不定长数据,正确的从串口返回数据:
【总结】
用定时器与串口中断结合起来,完美的实现了串口的不定长接收。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !