电赛延期了,开学就没时间打了,写点博客记录一下调试过的模块,移植过的源码
# 在 CubeMX 上移植 Nano
# 在 CubeMX 上 添加 RealThread 软件包
在新建 CubeMX 工程的基础上,我们需要安装一个 软件包,一个 pdsc 的软件包, https://www.rt-thread.org/download/cube/RealThread.RT-Thread.pdsc
, 点击 help 中的 manage embedded software packs 选择 From URL, 在框中输入 网址,然后点击 check
点击 OK 回到 User Defined Packs Manager 界面,
再次点击 OK, CubeMX 自动连接服务器,获取包描述文件.
再次进入 anage embedded software packs 界面的时候 就会出现 RealThread 选项卡,
点击之后选择其中一个 (建议 3.1.3 及其以上) 点击 install now, 等待下载
中间会弹出一个使用许可,记得同意
# 在 CubxMX 上 添加 RT-Threard Nano 至 工程
打开,我们新建的基本 CubeMX 的基本工程,不会建立的点这里▶.
然后点击 Software Packs, 选择 select components, 进入 Software Packs Component Selector
勾上 RealThread, 然后 右边就会出现 RT-Trhead 及其版本号,我这里使用 3.1.5, 可以勾选是否使用 shell, kernel, 和 device (这个我没用过,因该是这两个月出的这里先不做 demo), 点击 OK 结束
PS: 如果没有左半边的的界面点击一下粉红色框起的地方点击
这时候就会在 工程的 最左侧栏最底部多出一个 Software Packs 的 选项 里面有 RealThread RT-Thread 3.1.5 (不同版本号会不同), 点击后 中间会出现 RTOS Kernel
和 RTOS shell
, 勾选上,底部就会出现参数栏
参数这里按需配置,我打开了小内存管理,其他的一切照常,debug 项会在 RT-Thread Nano 初始胡过程中,打印执行到哪死在哪。看需求进行配置
最后,由于我们使用了 RT-Thread Ta 会占用 systick 所以我们需要修改 系统的定时器,这里我改成 TIM5
到这我们 用 cubemx 移植 RT-Thread 就完成了,生成工程即可,不过需要注释掉 cubeMX 生成的 HardFault_Handler
中断,
# 配置 Shell
kernel 配置 完毕,但是编译的时候 Keil 会报一个 未定义的 error
这里有我写好了的 串口输出代码,相关宏自行修改
void rt_hw_console_output(const char *str) | |
{ | |
rt_size_t i = 0, size = 0; | |
char a = '\r'; | |
__HAL_UNLOCK(&UartHandle); | |
size = rt_strlen(str); | |
for (i = 0; i < size; i++) | |
{ | |
if (*(str + i) == '\n') | |
{ | |
HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1); | |
} | |
HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1); | |
} | |
} |
这是 shell 未定义 输出函数导致的问题,当然当你写好了 输出函数后,他又会报另一个错
因为这个,函数在这里被调用了
之所以 接收函数 没有报错是因为,在 链接的时候 输出函数在 输入函数之前,在检测出函数异常后,直接中断链接过程
接收函数 就比较麻烦。除了接收中断之外 为了保证 接收的数据的稳定性,我们需要加上 一个 环形缓冲区,所以代码量就会上升一些
这里我就不细说了,直接贴个代码
# ringbuffer.h
环形缓冲区头文件
/** | |
* @file ringbuffer.h | |
* @author BlackSheep (ywz_123xxx@163.com) | |
* @brief 环形缓冲区 | |
* @version 0.1 | |
* @date 2021-05-16 | |
* | |
* @copyright Copyright (c) 2021 | |
* | |
*/ | |
#ifndef __RINGBUFFER_H__ | |
#define __RINGBUFFER_H__ | |
#include "stdint.h" | |
#define ALIGN_SIZE 4 // 字节对齐 | |
// * 返回对齐数 | |
#define ALIGN_DOWN(size, align) ((size) & ~((align)-1)) | |
#define ringbuffer_space_len(rb) ((rb)->buffer_size - ringbuffer_data_len(rb)) | |
struct ringbuffer | |
{ | |
uint8_t *buffer_ptr; | |
uint16_t read_mirror : 1; | |
uint16_t read_index : 15; | |
uint16_t write_mirror : 1; | |
uint16_t write_index : 15; | |
int16_t buffer_size; | |
}; | |
enum ringbuffer_state | |
{ | |
RINGBUFFER_EMPTY, | |
RINGBUFFER_FULL, | |
/* half full is neither full nor empty */ | |
RINGBUFFER_HALFFULL, | |
}; | |
void ringbuffer_init(struct ringbuffer *rb, uint8_t *pool, int16_t size); | |
uint32_t ringbuffer_data_len(struct ringbuffer *rb); | |
enum ringbuffer_state ringbuffer_status(struct ringbuffer *rb); | |
uint32_t ringbuffer_getchar(struct ringbuffer *rb, uint8_t *ch); | |
uint32_t ringbuffer_putchar(struct ringbuffer *rb, const uint8_t ch); | |
#endif |
# ringbuffer.c
环形缓冲区 .c 文件
/** | |
* @brief 获取 ringbuffer 状态 | |
* | |
* @param rb 缓冲区对象 | |
* | |
* @retval ringbuffer 枚举 | |
* 空 : RINGBUFFER_EMPTY | |
* 满 : RINGBUFFER_FULL | |
* 半满: RINGBUFFER_HALFFULL | |
* @date 2021-05-16 | |
*/ | |
inline enum ringbuffer_state ringbuffer_status(struct ringbuffer *rb) | |
{ | |
if (rb->read_index == rb->write_index) | |
{ | |
if (rb->read_mirror == rb->write_mirror) | |
{ | |
return RINGBUFFER_EMPTY; | |
} | |
else | |
{ | |
return RINGBUFFER_FULL; | |
} | |
} | |
return RINGBUFFER_HALFFULL; | |
} | |
/** | |
* @brief 获取缓冲区中数据大小 | |
* | |
* @param rb 缓冲区对象 | |
* | |
* @retval 缓冲区中现有数据 | |
* | |
* @date 2021-05-16 | |
*/ | |
uint32_t ringbuffer_data_len(struct ringbuffer *rb) | |
{ | |
switch (ringbuffer_status(rb)) | |
{ | |
case RINGBUFFER_EMPTY: | |
return 0; | |
case RINGBUFFER_FULL: | |
return rb->buffer_size; | |
case RINGBUFFER_HALFFULL: | |
default: | |
if (rb->write_index > rb->read_index) | |
{ | |
return rb->write_index - rb->read_index; | |
} | |
else | |
{ | |
return rb->buffer_size - (rb->read_index - rb->write_index); | |
} | |
}; | |
} | |
/** | |
* @brief 初始化 ringbuffer 实例 | |
* | |
* @param rb 缓冲区对象 | |
* @param pool 缓冲区数组首地址 | |
* @param size 缓冲区数组大小 | |
* | |
* | |
* @date 2021-05-16 | |
*/ | |
void ringbuffer_init(struct ringbuffer *rb, | |
uint8_t *pool, | |
int16_t size) | |
{ | |
/* 初始化读写索引 */ | |
rb->read_mirror = rb->read_index = 0; | |
rb->write_mirror = rb->write_index = 0; | |
/* 配置缓冲区首地址 和 大小 */ | |
rb->buffer_ptr = pool; | |
rb->buffer_size = ALIGN_DOWN(size, ALIGN_SIZE); | |
} | |
/** | |
* @brief 缓冲区存入 1 字节 | |
* | |
* @param rb 缓冲区对象 | |
* @param ch 存入缓冲区的数据 | |
* | |
* @retval 执行结果 | |
* 0: error | |
* 1: succee | |
* @date 2021-05-16 | |
*/ | |
uint32_t ringbuffer_putchar(struct ringbuffer *rb, const uint8_t ch) | |
{ | |
/* 缓冲区满 */ | |
if (!ringbuffer_space_len(rb)) | |
return 0; | |
rb->buffer_ptr[rb->write_index] = ch; | |
/* 翻转检测 */ | |
if (rb->write_index == rb->buffer_size - 1) | |
{ | |
rb->write_mirror = ~rb->write_mirror; | |
rb->write_index = 0; | |
} | |
else | |
{ | |
rb->write_index++; | |
} | |
return 1; | |
} | |
/** | |
* @brief 从缓冲区中读取 1 字节 | |
* | |
* @param rb 缓冲区实体 | |
* @param ch 读取的字节 | |
* | |
* @retval 执行结果: | |
* 0: error | |
* 1: succee | |
* | |
* @date 2021-05-16 | |
*/ | |
uint32_t ringbuffer_getchar(struct ringbuffer *rb, uint8_t *ch) | |
{ | |
/* 缓存区为 NULL */ | |
if (!ringbuffer_data_len(rb)) | |
return 0; | |
/* 存入字节 */ | |
*ch = rb->buffer_ptr[rb->read_index]; | |
if (rb->read_index == rb->buffer_size - 1) | |
{ | |
rb->read_mirror = ~rb->read_mirror; | |
rb->read_index = 0; | |
} | |
else | |
{ | |
rb->read_index++; | |
} | |
return 1; | |
} |
# FinSHDriver.h
FinSH 串口驱动头文件
#include <rtthread.h> | |
#include "ringbuffer.h" | |
// tShell | |
#define SHELL_USING_UART1 1 | |
#define SHELL_USING_UART2 0 | |
#define SHELL_USING_UART3 0 | |
#define SHELL_USING_UART4 0 | |
#define SHELL_USING_UART5 0 | |
#define SHELL_USING_UART6 0 | |
#define SHELL_USING_UART7 0 | |
#define SHELL_USING_UART8 0 | |
#define UART_RX_BUF_LEN 16 | |
#if SHELL_USING_UART1 | |
#define FIN_SHELL_UART USART1 // * tShell 串口 | |
#define FIN_SHELL_IRQn USART1_IRQn // * tShell 中断号 | |
#define TSHELL_IRQHandler USART1_IRQHandler // * tShell 中断服务函数 | |
#endif | |
#if SHELL_USING_UART2 | |
#define FIN_SHELL_UART USART2 // * tShell 串口 | |
#define FIN_SHELL_IRQn USART2_IRQn // * tShell 中断号 | |
#define TSHELL_IRQHandler USART2_IRQHandler // * tShell 中断服务函数 | |
#define TSHELL_AF GPIO_AF7_USART2 | |
#endif | |
#define FIN_SHELL_BAUDRATE 115200 // * tShell 波特率 |
# FinSHDriver.c
FinSH 源文件
/** | |
* @brief 移植控制台,实现控制台输出, | |
* 对接 rt_hw_console_output | |
* | |
* @param str 需要输出的 字符 | |
* | |
* | |
* @date 2021-07-30 | |
*/ | |
void rt_hw_console_output(const char *str) | |
{ | |
rt_size_t i = 0, size = 0; | |
char a = '\r'; | |
__HAL_UNLOCK(&UartHandle); | |
size = rt_strlen(str); | |
for (i = 0; i < size; i++) | |
{ | |
if (*(str + i) == '\n') | |
{ | |
HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1); | |
} | |
HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1); | |
} | |
} | |
/** | |
* @brief 移植 FinSH, 实现命令行交互 中断方式 | |
* 然后再对接 rt_hw_console_getchar | |
* | |
* @retval | |
* | |
* @date 2021-07-30 | |
*/ | |
char rt_hw_console_getchar(void) | |
{ | |
char ch = 0; | |
/* 从 ringbuffer 中拿出数据 */ | |
while (ringbuffer_getchar(&uart_rxcb, (rt_uint8_t *)&ch) != 1) | |
{ | |
rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER); | |
} | |
return ch; | |
} | |
/** | |
* @brief tshell 接收 | |
* | |
* | |
* @date 2021-07-30 | |
*/ | |
void TSHELL_IRQHandler(void) | |
{ | |
int ch = -1; | |
rt_base_t level; | |
/* enter interrupt */ | |
rt_interrupt_enter(); // 在中断中一定要调用这对函数,进入中断 | |
if ((__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET) && | |
(__HAL_UART_GET_IT_SOURCE(&(UartHandle), UART_IT_RXNE) != RESET)) | |
{ | |
while (1) | |
{ | |
ch = -1; | |
if (__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET) | |
{ | |
ch = UartHandle.Instance->DR & 0xff; | |
} | |
if (ch == -1) | |
{ | |
break; | |
} | |
/* 读取到数据,将数据存入 ringbuffer */ | |
ringbuffer_putchar(&uart_rxcb, ch); | |
} | |
rt_sem_release(&shell_rx_sem); | |
} | |
/* leave interrupt */ | |
rt_interrupt_leave(); // 在中断中一定要调用这对函数,离开中断 | |
} |
到这里,才算是移植结束,Nano 可带命令行 操作
大道五十,天衍四十九,人遁其一!