uart驱动描述基于GD32F470芯片。
rt-thread提供了一套I/O设备模型,如果想要使用操作系统的驱动去进行操作,就得将具体芯片的硬件驱动注册到设备驱动框架上。
关于rt-thread的I/O设备模型相关内容可以参考
rt-thread I/O设备模型-CSDN博客文章浏览阅读554次,点赞21次,收藏5次。事实上即便有操作系统也是可以直接使用裸机操作方式去操作的,但是这样做有几个缺点,一是代码看起来比较不统一,显得很乱,二是软件没有分层,耦合严重,不利于维护,三是裸机操作属于不受操作系统管控的部分,在对硬件资源的占用上,裸机操作可能会与操作系统操作产生冲突导致异常。实际上具体设备的驱动和操作系统的驱动函数是有个对应的,而用来实现这种对应的就是rt_device_register()函数,通过这个函数就可以将种类繁多的驱动注册到设备驱动框架上,之后就可以使用统一的操作函数了。可以看到最终访问设备用的是。https://blog.csdn.net/u011436603/article/details/136492057?spm=1001.2014.3001.5501 本次主要描述如何实现GD32F470的uart设备驱动
首先我们查看一下rt-thread的官方驱动文件serial.c,这里面有很多关于串口的函数,包括串口设备注册函数,串口设备操作函数,串口中断函数等。这个文件里的函数都是抽象的,并没有具体的硬件操作方法,我们要做的就是给它赋予实际的硬件操作能力。
来看一下串口设备的结构,包含了通用设备类型,串口操作函数,串口配置以及两个空类型的指针函数。
操作函数包含配置,控制,发送,接收,还有个dma传输。
配置包含uart常见的配置参数,如波特率,数据位等。
struct rt_serial_device
{struct rt_device parent;const struct rt_uart_ops *ops;struct serial_configure config;void *serial_rx;void *serial_tx;
};struct rt_uart_ops
{rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);int (*putc)(struct rt_serial_device *serial, char c);int (*getc)(struct rt_serial_device *serial);rt_size_t (*dma_transmit)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction);
};struct serial_configure
{rt_uint32_t baud_rate;rt_uint32_t data_bits :4;rt_uint32_t stop_bits :2;rt_uint32_t parity :2;rt_uint32_t bit_order :1;rt_uint32_t invert :1;rt_uint32_t bufsz :16;rt_uint32_t reserved :6;
};
接下来我们先注册串口驱动,首先定义一个数据结构 gd32_uart,用于具体硬件初始化用,再定义一个串口设备变量。
struct gd32_uart
{ uint32_t uart_periph; //Todo: 3bitsIRQn_Type irqn; //Todo: 7bits rcu_periph_enum per_clk; //Todo: 5bitsrcu_periph_enum tx_gpio_clk; //Todo: 5bitsrcu_periph_enum rx_gpio_clk; //Todo: 5bitsuint32_t tx_port; //Todo: 4bitsuint16_t tx_af; //Todo: 4bitsuint16_t tx_pin; //Todo: 4bitsuint32_t rx_port; //Todo: 4bitsuint16_t rx_af; //Todo: 4bitsuint16_t rx_pin; //Todo: 4bits
};static struct gd32_uart uarts =
{USART0, // uart peripheral indexUSART0_IRQn, // uart iqrnRCU_USART0, RCU_GPIOA, RCU_GPIOA, // periph clock, tx gpio clock, rt gpio clockGPIOA, GPIO_AF_7, GPIO_PIN_9, // tx port, tx alternate, tx pinGPIOA, GPIO_AF_7, GPIO_PIN_10, // rx port, rx alternate, rx pin}struct rt_serial_device serial0;
接下来是实现操作系统设备驱动的各个函数,包括configure,control,getc,putc,至于dma_transmit我们暂时没用到,然后完成设备注册。
void gd32_uart_gpio_init(struct gd32_uart *uart)
{/* enable USART clock */rcu_periph_clock_enable(uart->tx_gpio_clk);rcu_periph_clock_enable(uart->rx_gpio_clk);rcu_periph_clock_enable(uart->per_clk);/* connect port to USARTx_Tx */gpio_af_set(uart->tx_port, uart->tx_af, uart->tx_pin);/* connect port to USARTx_Rx */gpio_af_set(uart->rx_port, uart->rx_af, uart->rx_pin);/* configure USART Tx as alternate function push-pull */gpio_mode_set(uart->tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->tx_pin);gpio_output_options_set(uart->tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->tx_pin);/* configure USART Rx as alternate function push-pull */gpio_mode_set(uart->rx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->rx_pin);gpio_output_options_set(uart->rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->rx_pin);NVIC_SetPriority(uart->irqn, 0);NVIC_EnableIRQ(uart->irqn);
}static rt_err_t gd32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{struct gd32_uart *uart;RT_ASSERT(serial != RT_NULL);RT_ASSERT(cfg != RT_NULL);uart =(struct gd32_uart *) serial->parent.user_data;gd32_uart_gpio_init(uart);usart_baudrate_set(uart->uart_periph, cfg->baud_rate);switch (cfg->data_bits){case DATA_BITS_9:usart_word_length_set(uart->uart_periph, USART_WL_9BIT);break;default:usart_word_length_set(uart->uart_periph, USART_WL_8BIT);break;}switch (cfg->stop_bits){case STOP_BITS_2:usart_stop_bit_set(uart->uart_periph, USART_STB_2BIT);break;default:usart_stop_bit_set(uart->uart_periph, USART_STB_1BIT);break;}switch (cfg->parity){case PARITY_ODD:usart_parity_config(uart->uart_periph, USART_PM_ODD);break;case PARITY_EVEN:usart_parity_config(uart->uart_periph, USART_PM_EVEN);break;default:usart_parity_config(uart->uart_periph, USART_PM_NONE);break;}usart_receive_config(uart->uart_periph, USART_RECEIVE_ENABLE);usart_transmit_config(uart->uart_periph, USART_TRANSMIT_ENABLE);usart_enable(uart->uart_periph);return RT_EOK;
}static rt_err_t gd32_control(struct rt_serial_device *serial, int cmd, void *arg)
{struct gd32_uart *uart;RT_ASSERT(serial != RT_NULL);uart =(struct gd32_uart *) serial->parent.user_data;switch (cmd){case RT_DEVICE_CTRL_CLR_INT:/* disable rx irq */NVIC_DisableIRQ(uart->irqn);/* disable interrupt */usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE);break;case RT_DEVICE_CTRL_SET_INT:/* enable rx irq */NVIC_EnableIRQ(uart->irqn);/* enable interrupt */usart_interrupt_enable(uart->uart_periph, USART_INT_RBNE);break;}return RT_EOK;
}static int gd32_putc(struct rt_serial_device *serial, char ch)
{struct gd32_uart *uart;RT_ASSERT(serial != RT_NULL);uart =(struct gd32_uart *) serial->parent.user_data;usart_data_transmit(uart->uart_periph, ch);while((usart_flag_get(uart->uart_periph, USART_FLAG_TC) == RESET));return 1;
}static int gd32_getc(struct rt_serial_device *serial)
{int ch;struct gd32_uart *uart;RT_ASSERT(serial != RT_NULL);uart =(struct gd32_uart *) serial->parent.user_data;ch = -1;if (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET)ch = usart_data_receive(uart->uart_periph);return ch;
}static const struct rt_uart_ops gd32_uart_ops =
{gd32_configure,gd32_control,gd32_putc,gd32_getc,
};int gd32_hw_usart_init(void)
{struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;serial0.ops = &gd32_uart_ops;config.baud_rate=BAUD_RATE_9600;config.data_bits=DATA_BITS_8;config.stop_bits=STOP_BITS_1;config.parity=PARITY_NONE;serial0.config = config;rt_hw_serial_register(&serial0,"uart0",RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,(void *)&uarts);return 0;
}
INIT_BOARD_EXPORT(gd32_hw_usart_init);
完成上面这些基本就能够使用uart了,但是因为我们配置了串口中断,所以我们还需要去实现串口中断函数。
static void uart_isr(struct rt_serial_device *serial)
{struct gd32_uart *uart;RT_ASSERT(serial != RT_NULL);uart =(struct gd32_uart *) serial->parent.user_data;/* UART in mode Receiver -------------------------------------------------*/if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RBNE) != RESET) &&(usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET)){rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);/* Clear RXNE interrupt flag */usart_flag_clear(uart->uart_periph, USART_FLAG_RBNE);}
}void USART0_IRQHandler(void)
{/* enter interrupt */rt_interrupt_enter();uart_isr(&serial0);/* leave interrupt */rt_interrupt_leave();
}
现在,uart驱动已经注册完毕,剩余的就是具体的应用功能了。这里为了减少篇幅,只列举了单串口,例程里提供了7个串口的驱动,有兴趣可以看看https://download.csdn.net/download/u011436603/88914288?spm=1001.2014.3001.5503
下面简单介绍一种应用的方式
首先通过rt_device_find去找到uart设备,使用rt_device_open去打开设备。注意,在rt_device_open打开设备时,如果首次打开,会调用device_init进行初始化,此时才会执行gd32_configure进行串口的初始化。
之后可以创建两个线程,一个发送一个接收,通过操作系统提供的事件来进行同步。发送线程时刻等待发送事件,一旦接收到发送事件则开始发送数据。接收线程时刻等待接收事件,一旦接收到接收事件则从串口中读数据填入缓存,以备后续处理。