【串口收发不定长数据】使用中断的方式—以AT32为例
使用中断方式
这种在数据接收不频繁状态下使用,简单易实现。
既然是使用中断,所以自然需要硬件的支持,比如AT32,stm32就可以这么做,关于USART具体的原理、配置及使用详见:【雅特力AT32】串口入门实战:轮询、中断、SWAP(收发管脚交换)功能
关键代码
uint8_t Serial_RxFlag; // 定义串口接收的标志位变量
char rxdata[20]; // 接收数据缓存区
uint8_t rx_dat; // 接收字节转运
uint32_t rx_pointer; // 缓存区指针
/**
* 函 数:USART收发中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异且与使能函数对应,否则中断函数将不能进入卡死(特别注意接收中断使能后测试)
* USART_RDBF_FLAG USART_TDC_FLAG
*/
void USART2_IRQHandler(void)
{
if(usart_flag_get(MY_USART, USART_RDBF_FLAG) != RESET) // 接收数据缓冲区满标志
{
Serial_RxFlag = 1;
/* 读取并存放数据寄存器的数据 */
rx_dat = usart_data_receive(MY_USART);
rxdata[rx_pointer++] = rx_dat;
}
}
/**
* 函 数:USART接收不定长数据并进行发送测试
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为USART接收数据的处理函数
* 可以放在主函数测试,xcom发送任意信息,AT32接收后再转发给xcom
* 注意测试字符串长度与temp的大小,可自定义更改
*/
void usart_proc(void)
{
if(rx_pointer != 0)
{
uint16_t rx_test = rx_pointer;
delay_ms(1);
/* 确保数据包已传输完毕 */
if(rx_pointer == rx_test)
{
/* 打印接收的数据 */
Serial_SendArray((uint8_t *)rxdata, strlen(rxdata));
/* 更新缓冲区及指针状态 */
rx_pointer = 0;
memset(rxdata,0,20);
}
}
}
完整代码
my_usart.c
以AT32为例,包括串口收发配置、中断接收;发送字符(串)、数组、数字的函数封装。
#include "my_usart.h"
uint8_t Serial_RxData; // 定义串口接收的数据变量
uint8_t Serial_RxFlag; // 定义串口接收的标志位变量
char rxdata[20]; // 接收数据缓存区
uint8_t rx_dat; // 接收字节转运
uint32_t rx_pointer; // 缓存区指针
#define MY_USART USART2
/**
* @brief config usart
* @param none
* @retval none
*/
void usart_configuration(void)
{
gpio_init_type gpio_init_struct;
/* enable the usart2 and gpio clock */
crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_init_struct);
/* configure the usart2 tx, rx pin */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
gpio_init_struct.gpio_pins = GPIO_PINS_2 | GPIO_PINS_3;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE2, GPIO_MUX_7); //tx
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE3, GPIO_MUX_7); //rx
/* config usart nvic interrupt */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(USART2_IRQn, 0, 0);
/* configure usart2 param */
usart_init(MY_USART, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
/* USART2 Send and receive enable */
usart_transmitter_enable(MY_USART, TRUE);
usart_receiver_enable(MY_USART, TRUE);
/* enable usart2 and usart3 interrupt */
usart_interrupt_enable(MY_USART, USART_RDBF_INT, TRUE);
usart_enable(MY_USART, TRUE);
}
/**
* 函 数:USART2收发中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异且与使能函数对应,否则中断函数将不能进入卡死(特别注意接收中断使能后测试)
* USART_RDBF_FLAG USART_TDC_FLAG
*/
void USART2_IRQHandler(void)
{
if(usart_flag_get(MY_USART, USART_RDBF_FLAG) != RESET) // 接收数据缓冲区满标志
{
Serial_RxFlag = 1;
/* 读取并存放数据寄存器的数据 */
rx_dat = usart_data_receive(MY_USART);
rxdata[rx_pointer++] = rx_dat;
}
}
/**
* 函 数:USART接收不定长数据并进行发送测试
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为USART接收数据的处理函数
* 可以放在主函数测试,xcom发送任意信息,AT32接收后再转发给xcom
* 注意测试字符串长度与temp的大小,可自定义更改
*/
void usart_proc(void)
{
if(rx_pointer != 0)
{
uint16_t rx_test = rx_pointer;
delay_ms(1);
/* 确保数据包已传输完毕 */
if(rx_pointer == rx_test)
{
/* 打印接收的数据 */
Serial_SendArray((uint8_t *)rxdata, strlen(rxdata));
/* 更新缓冲区及指针状态 */
rx_pointer = 0;
memset(rxdata,0,20);
}
}
}
/**
* 函 数:串口发送一个数组
* 参 数:data 要发送数组的首地址
* 参 数:size 要发送数组的长度
* 返 回 值:无
*/
void UART_Write_Blocking(uint8_t* data,uint16_t size)
{
uint16_t i = 0;
while(usart_flag_get(MY_USART, USART_TDBE_FLAG) == RESET); //传输数据缓冲区空标志
usart_flag_clear(MY_USART, USART_TDBE_FLAG);
for(i=0;i<size;i++)
{
usart_data_transmit(MY_USART, (uint16_t)data[i]);
while(usart_flag_get(MY_USART, USART_TDC_FLAG) == RESET);
usart_flag_clear(MY_USART,USART_TDC_FLAG);
}
}
/**
* 函 数:串口发送一个字节
* 参 数:Byte 要发送的一个字节
* 返 回 值:无
*/
void Serial_SendByte(uint8_t Byte)
{
while(usart_flag_get(MY_USART, USART_TDBE_FLAG) == RESET); //传输数据缓冲区空标志
usart_flag_clear(MY_USART, USART_TDBE_FLAG);
usart_data_transmit(MY_USART, Byte);
while(usart_flag_get(MY_USART, USART_TDC_FLAG) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
usart_flag_clear(MY_USART,USART_TDC_FLAG);
}
/**
* 函 数:串口发送一个数组
* 参 数:Array 要发送数组的首地址
* 参 数:Length 要发送数组的长度
* 返 回 值:无
*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Array[i]);
}
}
/**
* 函 数:串口发送一个字符串
* 参 数:String 要发送字符串的首地址
* 返 回 值:无
*/
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}
/**
* 函 数:次方函数(内部使用)
* 返 回 值:返回值等于X的Y次方
*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}
/**
* 函 数:串口发送数字
* 参 数:Number 要发送的数字,范围:0~4294967295
* 参 数:Length 要发送数字的长度,范围:0~10
* 返 回 值:无
*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}