最近在N32G457芯片学习使用RT_thread时, 尝试485通讯.于是加载了rs485这个pkg.
rt_thread: https://github.com/RT-Thread/rt-thread.git
切换到 4.0.x
N32G457驱动: https://github.com/RT-Thread/rt-thread/tree/master/bsp/n32g452xx/Libraries/rt_drivers
发现串口接收到数据后, 串口中断持续进入. 清除中断标志没有效果.
一番操作后, 发现串口中断服务函数里取到的uart已经不是注册时给的uart了
int rt_hw_usart_init(void)
{
struct n32_uart *uart;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
#if defined(BSP_USING_UART1)
uart = &uart1;
config.baud_rate = BAUD_RATE_115200;
serial1.ops = &n32_uart_ops;
serial1.config = config;
NVIC_Configuration(uart);
/* register UART1 device */
rt_hw_serial_register(&serial1, "uart1",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX |
RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_DMA_RX,
uart);
#endif /* BSP_USING_UART1 */
...
------------------------------------------------------------------------
static void uart_isr(struct rt_serial_device *serial)
{
struct n32_uart *uart = (struct n32_uart *) serial->parent.user_data;
RT_ASSERT(uart != RT_NULL);
...
rt_hw_serial_register 注册时, 会将serial.parent.user_data赋值为uart.
而 uart_isr 函数里, uart从serial.parent.user_data取出时, 已经变了.
原因是 rs485这个包 在rs485.c 的 int rs485_connect(rs485_inst_t * hinst)函数里对serial.parent.user_data重新赋值冲掉了.
hinst->serial->user_data = hinst;
hinst->serial->rx_indicate = rs485_recv_ind_hook;
hinst->status = 1;
LOG_D("rs485 connect success.");
rs485_connect 函数里把 hinst->serial->user_data = hinst是为了在接收中断回调函数rs485_recv_ind_hook里进行使用.
static rt_err_t rs485_recv_ind_hook(rt_device_t dev, rt_size_t size)
{
rs485_inst_t *hinst = (rs485_inst_t *)(dev->user_data);
if (hinst->evt)
{
rt_event_send(hinst->evt, RS485_EVT_RX_IND);
}
return(RT_EOK);
}
驱动层与驱动上层都使用了user_data, 导致冲突.
经过与rs485包的作者沟通
https://github.com/qiyongzhong0/rt-thread-rs485/issues/2
他觉得 user_data是给驱动上层应用预留的,驱动占用是不合理的.
但是之后查阅了 RT_thead/bsp/下的很多bsp层drivers/drv_uart.c uart注册为seril设备时的情况 int rt_hw_uart_init(void)
allwinner_tina, amebaz, amp32, asm9260t, at32, at91sam9g45,...raspberry-pi, 等都是将uart作为user_data的.
而 stm32, 是以NULL传入的. 这样的驱动是少数的.
大部分芯片移植的时候, 都在驱动层使用了user_data.这就导致驱动上传应用会因为user_data问题,导致驱动无法兼容.
目前的rt_device设计
/**
* Device structure
*/
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
...
void *user_data; /**< device private data */
};
不加区分的user_data导致了驱动与应用不兼容.
rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
const char *name,
rt_uint32_t flag,
void *data)
注册函数里提供了data.入参. 且大部分驱动都把芯片资源作为data传给了驱动.
/**
* Device structure
*/
struct rt_device
{
struct rt_object parent; /**< inherit from rt_object */
...
void *user_data; /**< 留给驱动 */
void *device_data; /**< 留给驱动上层 */
};
论坛里搜索了一下发现有人提出了这个问题.
https://club.rt-thread.org/ask/question/428675.html
应该选择方案2, user_data留给驱动的上层.
驱动实现时, struct stm32_uart, 等这些是在drv_uart.c内部定义的.
struct stm32_uart
{
...
struct rt_serial_device serial;
};
在uart结构体内部定义serial设备. 当给出serial指针时, 可以通过
uart = rt_container_of(serial, struct stm32_uart, serial);
来计算出uart的位置.
serial定义在uart内部, 实现了serial与uart对应关系. 是不需要user_data来传递的.
serial定义在uart内部, struct stm32_uart 结构体只在 drv_uart.c里出现, 驱动外部是无法知道这个结构体的.
即使有了serial指针, 是无法计算出uart指针的. 无法直接对uart进行任意操作.
如果使用user_data来传递, 这样就能在驱动外面, 通过device->user_data就能访问到uart, 就能直接进行任意操作.
这是不安全的.
定义struct stm32_uart时,将struct rt_serial_device serial定义为结构体第一个元素,这样直接类型转换就行了,不用在计算uart指针。