最近工作需要重新捡起 rtt 使用.之前使用的时候都是智能车比赛要求使用,用的也是逐飞科技移植好的工程,我自己都没有移植过.后来工作需要时发现自己一点都不懂,一脸懵逼.折磨一遍后再回顾,感觉其实也挺简单的.
官方说明 (教程) 手册 : RT-Thread文档中心 (https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-nano/an0038-nano-introduction)
官方 api 手册 : RT-Thread API参考手册 (https://www.rt-thread.org/document/api/group___i_p_c.html)
rtos的人来说,还是有点不够详细.Nano版, 俗称就是最简版,默认只包含最基本的线程 , 信号量 , 队列 , 邮箱等基础功能,不包含其他工具包, 比如finsh控制台.移植方便,因为只需要设置系统时钟部分的接口.如果你还不了解线程,信号量,finsh控制台等概念. 先去看看说明视频,这部分不是本文重点,不赘述.
总有适合你的 : RT-Tread 视频中心 (https://www.rt-thread.org/page/video.html)
rt-thread 文件夹,得到下面的内容.看着眼花缭乱,很多东西,其它下方的非文件夹文件都是github的相关文件,和rtt没关系,忽略.主要看上面一排文件夹的内容.bsp : 是板级接口文件,也就是 board.c 和 rtconfig.h, 看名字翻译就知道,前者是板级接口,后者的rt系统配置,里面全是宏定义开关.而其它文件夹都是一些常用单片机移植好的例子,不需要参考就直接删除.components : 看翻译就知道,里面装着一些rtt系统的组件,nano版中,这里面就一个finsh控制台组件和一个device组件,后者没用过,好像是硬件接口,略.主要看finsh控制台组件.最后移植好rtt后再根据需要添加组件.如果单片机SRAM抓急,一般就放不下finsh..h 头文件集合..c 脚本文件集合.include 和 src 不需要改动,板级只保留两个代码文件,组件只保留finsh文件夹,内核只保留需要的.最后得到的文件夹内容如下.然后整个文件夹丢到工程里即可.$PROJ_DIR$\..\rtthread\include\libc
$PROJ_DIR$\..\rtthread\include
$PROJ_DIR$\..\rtthread\bsp
HardFault_Handler : 是单片机硬件错误中断函数, rtt系统有自带的硬件中断错误,如果线程跑飞会进入,同时反馈错误信息.
SysTick_Handler : 是单片机滴答定时器中断函数.rtt系统会使用滴答定时器作为系统节拍.这意味着单片机运行时将不能另外使用滴答定时器,同时在main之前要初始化好系统时钟和滴答定时器.之后再修改.
__low_level_init : 是单片机启动函数,就是调用main函数的函数.rtt在调用main之前会做一系列初始化操作.
知道函数名字后就直接打开全局搜索,找函数位置.然后屏蔽掉就可以了.再编译就没有错了.安心~
board.c文件中的rt_hw_board_init()函数. /* System Clock Update */
SystemCoreClockUpdate();
比如我使用的hc32,裸机例程默认4Mhz,如果不修改成全速的48Mhz,貌似不能正常运行.
关于嵌入式单片机时钟树的知识我比较薄弱,而且不同单片机平台差异巨大,请自己找资料尝试修改吧.
board.c文件中的rt_hw_board_init()函数. /* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
board.c文件中.这个根据单片机而异.直接找厂家例程里的滴答定时器例程,复制粘贴即可.官方说明手册 : 使线程睡眠
在实际应用中,我们有时需要让运行的当前线程延迟一段时间,在指定的时间到达后重新运行,这就叫做 “线程睡眠”。线程睡眠可使用以下三个函数接口:
rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);
void __delay10us(uint32_t u32Cnt)
{
for (int j=0; j<u32Cnt; j++)
for (int i=0; i<(SystemCoreClock/1000000); i++);
// SystemCoreClock 是当前系统时钟频率
}
void main(void)
{
while (1)
rt_thread_mdelay(100);
}
$PROJ_DIR$\..\rtthread\components\finsh
rtconfig.h文件内去掉注释,但是我没看到我下载的rtconfig.h文件有这行注释,所以自行添加算了.接下来开始实现接口函数,总结来说就是要实现finsh控制台的 发送字符串函数,接收字符串函数,进行串口通信初始化.
串口初始化属于板级功能,而且要在rtt系统运行前执行,所以是和系统时钟初始化放一起就可以了.也就是 rt_hw_board_init() 函数里.rtt也提供隐式执行,调用宏定义即可.
发送字符串函数功能的实现拷贝单片机库例程即可.接收字符串函数也是.唯一需要注意的是接收字符串要在接收完所有后再传入,而不是一个个传入,不然不能正确识别指令.
finsh控制台如果接收到不能识别内容会原模原样返回发送,用于自行判断.如果发现指令没错,那可能是最后没有加换行符,指令的最后要加换行才会被识别.
推荐参考 : 移植示例代码
/* 第一部分:ringbuffer 实现部分 */
// 略,不需要修改
/* 第二部分:finsh 移植对接部分 */
// 略,变量定义部分
/* 初始化串口,中断方式 */
static int uart_init(void)
{
/* 初始化串口接收 ringbuffer */
rt_ringbuffer_init(&uart_rxcb, uart_rx_buf, UART_RX_BUF_LEN);
/* 初始化串口接收数据的信号量 */
rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0);
/* 初始化串口参数,如波特率、停止位等等 */
// 更改为自己单片机的
/* 初始化串口引脚等 */
// 更改为自己单片机的
/* 中断配置 */
// 更改为自己单片机的
return 0;
}
INIT_BOARD_EXPORT(uart_init);
/* 移植控制台,实现控制台输出, 对接 rt_hw_console_output */
void rt_hw_console_output(const char *str)
{
rt_size_t i = 0, size = 0;
char a = '\r';
size = rt_strlen(str);
for (i = 0; i < size; i++)
{
if (*(str + i) == '\n')
{
// 发送字符串 '\r' ,更改为自己单片机的
}
// 发送字符 str[i] ,更改为自己单片机的
}
}
/* 移植 FinSH,实现命令行交互, 需要添加 FinSH 源码,然后再对接 rt_hw_console_getchar */
/* 中断方式 */
// 略,不需要修改
/* uart 中断 */
void USART2_IRQHandler(void)
{
int ch = -1;
rt_base_t level;
/* enter interrupt */
rt_interrupt_enter(); //在中断中一定要调用这对函数,进入中断
// 略,判断中断标志位,并去除中断标志位
{
{
ch = -1;
// 略 获取字符
/* 读取到数据,将数据存入 ringbuffer */
rt_ringbuffer_putchar(&uart_rxcb, ch);
}
// 这比较特殊,需要获取完所有字符后,再调用释放信号量.
// 如果能一次中断获取完毕就在中断里调用,
// 如果不能,可以另外开一个定时器,记录接收超时,超时了就认为是接收完毕并调用即可.
rt_sem_release(&shell_rx_sem);
}
/* leave interrupt */
rt_interrupt_leave(); //在中断中一定要调用这对函数,离开中断
}
// 略,最后面这个是没用的.