RT-Thread的串口接收解析
RT-Thread的串口接收非常方便,只需要设置好接收回调函数,在回调函数中释放信号量,然后在读数线程中等待信号量,调用设备框架读取设备缓存即可,那底层到底怎么实现的呢,让我们来看看
1.设置回调函数
rt_device_set_rx_indicate(dev->serial,uart_input);//通过这个设置接收中断时要进行的工作,使用回调函数
2.函数调用路径
中断触发->USARTx_IRQHandler()【位于drv_usart】->uart_isr()【位于drv_usart】->rt_hw_serial_isr()【位于serial】->serial->parent.rx_indicate
3.Serial结构体的继承关系
rt_serial_device可以说是stm32_uart的父结构体,rt_device是rt_serial_device的父结构体,rt_object是rt_device的父结构体,这里类似C++的类的概念,但实际是结构体,不同点是结构体的成员需要赋值语句进行初始化,没有构造函数。
整理了以上结构体的定义,可以看到一层一层的包含关系。
/**
* Base structure of Kernel object
*/
struct rt_object
{
char name[RT_NAME_MAX]; /**< name of kernel object */
rt_uint8_t type; /**< type of kernel object */
rt_uint8_t flag; /**< flag of kernel object */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< list node of kernel object */
};
/**
* Device structure
*/
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
enum rt_device_class_type type; /**< device type */
rt_uint16_t flag; /**< device flag */
rt_uint16_t open_flag; /**< device open flag */
rt_uint8_t ref_count; /**< reference count */
rt_uint8_t device_id; /**< 0 - 255 */
/* device call back */
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
#ifdef RT_USING_DEVICE_OPS
const struct rt_device_ops *ops;
#else
/* common device interface */
rt_err_t (*init) (rt_device_t dev);
rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);
rt_err_t (*close) (rt_device_t dev);
rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif
#if defined(RT_USING_POSIX)
const struct dfs_file_ops *fops;
struct rt_wqueue wait_queue;
#endif
void *user_data; /**< device private data */
};
struct rt_serial_device
{
struct rt_device parent;
const struct rt_uart_ops *ops;
struct serial_configure config;
void *serial_rx;
void *serial_tx;
};
/* stm32 uart dirver class */
struct stm32_uart
{
UART_HandleTypeDef handle;
struct stm32_uart_config *config;
#ifdef RT_SERIAL_USING_DMA
struct
{
DMA_HandleTypeDef handle;
rt_size_t last_index;
} dma_rx;
struct
{
DMA_HandleTypeDef handle;
} dma_tx;
#endif
rt_uint16_t uart_dma_flag;
struct rt_serial_device serial;
};
串口收到数据后会通过rt_dma_recv_update_put_index放入rt_serial_rx_fifo,rt_serial_rx_fifo就是rt_serial_device类中的serial_rx成员(类型是rt_serial_rx_fifo类的指针)
/*
* Serial FIFO mode
*/
struct rt_serial_rx_fifo
{
/* software fifo */
rt_uint8_t *buffer;
rt_uint16_t put_index, get_index;
rt_bool_t is_full;
};
这里放一下别人的图,做的比较好我就直接拿来用了原文链接RT-Thread的串口V1接收
图1 中断接收
图2 DMA接收
rt_serial_rx_fifo在内存的位置分析
rt_serial_rx_fifo到底在内存什么位置呢,我找V1版本没有找到,有人在V2上似乎找到了RT-Thread-RT-Thread 串口驱动问题-缓存一致性RT-Thread问答社区
图3 SerialV2
V2版本是malloc申请的,不过提问者遇到了申请结果在DMA不能访问的DTCM的问题,好在他自己写了一个malloc解决了,话说回来V1怎么搞的呢,哈哈也找到了,也是malloc出来的。
图4 serialV1初始化
图5 serialV1申请fifo内存