基于
电子发烧友提供的RA2E1试用板,实现FreeRTOS下的串口接收不定长数据。
1.工程基于之前写的FreeRTOS写的点灯的工程。拷贝一份,重命名一下。导入工程,左上角文件,导入,常规,现有项目,选中拷贝后的工程。正常的话,中间会有一个你工程的名字。选中,点完成,编译一下,没错误。
2.uart本身的配置。左边项目管理器下的configura
tion.xml双击进入配置页面。这有两个部分,一个是配置uart这个硬件外设,包括他的GPIO/BAUD等等,另外一个是配置瑞萨提供的关于uart这个的接口,类似于HAL库。不配置的话,就是自己写操作函数。
1)SCI:瑞萨的uart他这个不叫uart,叫SCI,串行
通信接口。点击Pins,Peripherals,Connectify:SCI,发现GPIO不能使能。不慌,先看看,点一下Operation Mode,选中Asynchronous UART之后,下面会自动出现GPIO。他这个还可以使用其他的模式,暂未涉及。就这样配置就行,使用TX/RX这两个引脚是可以修改的。看实际需求。
2)配置UART Driver,这个驱动叫UART DRIVER,没打错。点Stacks,New Stack,Driver,Connectify,UART DRIVER。点一下就会多三个东西出来。g_uart0这个表示的是第一个uart,注意在这并不代表实实在在的硬件串口标号。当然他这个也支持DMA,不过他叫DTC,在这不使用。这一个就配置完了,没有啥实际的配置选项,更像是一个开关。
3)配置UART DRIVER下的SCI0,类似于添加具体的设备到驱动里面去。点击左边g_uart0 UART DRIVER,在下面的属性会出现具体的配置。要是没这个界面,点窗口显示视图,属性,之后就有了。Common中使能FIFO,使能DTC和Flow Contrl,就是不使用DMA和控制脚,控制脚一般是RS-485用的。Module里面的General中Name是UART的实际标号,代表这个UART。数据位8位,停止位1位,没有校验。Channel表示这是硬件上的第几个UART,和刚配置的SCI保持一致。在这是SCI 0就设置成0即可。Baud波特率设置成115200。Flow Control没使用,不用配置。Extral默认就行。Interrupts配置的是uart中断回调函数的名字和优先级。名字可以修改成你喜欢的,所有的关于这个串口的接收中断发送中断异常中断都会进入这个函数。在这保持默认即可。优先级看实际需求进行修改。
3.点击生成,然后生成代码,没错误,但如果改了中断回调函数的名字,这会报错,因为那个函数未定义。
4.写自己的接收回调函数。在led_entry.c的下面写自己的代码,或者自行添加一个c文件,或者找个有/* TODO: add your own code here */这个标识的地方写自己的代码。先写个回调函数看看会不会被触发,函数名void user_uart_callback(uart_callback_args_t *p_args);这个参数不是任意定义的,在hal_data.h有这个的原型,具体定义自己翻看.h文件。具体如下:void user_uart_callback(uart_callback_args_t *p_args)
{
uint8_t rcv_data;
uart_event_t event;
event=p_args->event;
switch(event)
{
case UART_EVENT_RX_CHAR:
{
rcv_data=(uint8_t)p_args->data;
break;
}
case UART_EVENT_TX_COMPLETE:
{
break;
}
default:
{
break;
}
}
}
注意uart这个需要自己初始化一下,g_uart0.p_api->open(g_uart0.p_ctrl,g_uart0.p_cfg);调用这一个函数。g_uart0是个句柄,里面包含很多接口和参数。连上线,通过串口发送数据,就会进入这个函数里面,否则表示配置失败,检查配置和连线。
4.写发送函数。uint8_t data[3]={0x01,0x02,0x03}; g_uart0.p_api->write(g_uart0.p_ctrl,data,3);发送数据和数据长度。发送完成之后,也会进入到刚那个回调函数。
5.通过AGT定时器模拟一个空闲中断。当接收到第一个字节时,开始倒计时,新接收到一个字节时,刷新时间,一直没接收到新的字节,表示这一个数据包接收完成。New Stack中添加agt的驱动,属性里面配置是否是周期性触发,定时周期,中断优先级,回调函数名称这些,再使能硬件AGT0。这个定时器的溢出时间可以在hal_data.c里面查看。定时器使用前也得开启一下,g_timer0.p_api->open(g_timer0.p_ctrl,g_timer0.p_cfg);。
6.创建一个uart 接收类型的数据类型。
typedef struct uart_rcv_data{
uint8_t data[UART_MAX_LEN];//接收到的数据
uint8_t len;//接收到的数据长度
uint8_t rcv_state;//接收状态 0未接收 1正在接收2接收完成
uint8_t rcv_tim_cnt;//用于空闲中断计时
uart_event_t event;//串口的中断类型
}c_uart_rcv_data_t;
7.空闲中断的间隔是UART_MAX_period*0.0027ms,单次最大可接收UART_MAX_LEN个字节。
8.下载之后接上串口。发送啥数据回啥数据。当然只是在128个字节以内,发送间隔不小于50ms,实测定时发送间隔100ms,单次发104字节,收发个10w字节丢了368字节,效果一般般,手点发送的话完全没丢包。这的话主要是介绍配置,应用的话是需要大幅优化的。
9.配置图片比较多,在这不一一截图了, 下面是源代码,实际应用还需考虑到其他问题。比如接收的缓冲区,在哪处理数据,怎么同步数据这些问题。在这写个思路吧。将接收到的数据通过消息队列发送出去,在另外一个线程进行处理。接收这边就只负责接收。发送的时候,一次发送太长数据,需要等待,判断发送是否完成。不然没发送完又发新的数据是不行的。接收和发送是共用一个回调函数,这两个的优先级哪个更高,也是需要考虑的。
#include "led.h"
/* New Thread entry function */
#define UART_MAX_LEN 128
#define UART_MAX_period 5
typedef struct uart_rcv_data{
uint8_t data[UART_MAX_LEN];
uint8_t len;
uint8_t rcv_state;
uint8_t rcv_tim_cnt;
uart_event_t event;
}c_uart_rcv_data_t;
c_uart_rcv_data_t uart0;
/* pvParameters contains TaskHandle_t */
void led_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
/* TODO: add your own code here */
g_uart0.p_api->open(g_uart0.p_ctrl,g_uart0.p_cfg);
g_timer0.p_api->open(g_timer0.p_ctrl,g_timer0.p_cfg);
g_timer0.p_api->start(g_timer0.p_ctrl);
g_timer0.p_api->stop(g_timer0.p_ctrl);
while(1)
{
vTaskDelay (10);
if( uart0.rcv_state == 2)
{
uart0.rcv_state =0;
g_uart0.p_api->write(g_uart0.p_ctrl,uart0.data,uart0.len);
uart0.len=0;
}
}
}
void user_uart_callback(uart_callback_args_t *p_args)
{
uart0.event=p_args->event;
switch(uart0.event)
{
case UART_EVENT_RX_CHAR:
{
if(uart0.rcv_state == 2)
{
return;
}
if(uart0.len < UART_MAX_LEN)
{
uart0.data[uart0.len]=(uint8_t)p_args->data;
uart0.len++;
uart0.rcv_tim_cnt=UART_MAX_period;
}
if(uart0.rcv_state == 0)
{
uart0.rcv_tim_cnt=UART_MAX_period;
g_timer0.p_api->start(g_timer0.p_ctrl);
}
break;
}
case UART_EVENT_TX_COMPLETE:
{
break;
}
default:
{
break;
}
}
}
void uart_timer_callback(timer_callback_args_t *p_args)
{
p_args=(void*)p_args;
if(uart0.rcv_tim_cnt > 0)
{
uart0.rcv_state =1;
uart0.rcv_tim_cnt--;
}
if(uart0.rcv_tim_cnt == 0)
{
uart0.rcv_state =2;
g_timer0.p_api->stop(g_timer0.p_ctrl);
}
}