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: 3bits
IRQn_Type irqn; //Todo: 7bits
rcu_periph_enum per_clk; //Todo: 5bits
rcu_periph_enum tx_gpio_clk; //Todo: 5bits
rcu_periph_enum rx_gpio_clk; //Todo: 5bits
uint32_t tx_port; //Todo: 4bits
uint16_t tx_af; //Todo: 4bits
uint16_t tx_pin; //Todo: 4bits
uint32_t rx_port; //Todo: 4bits
uint16_t rx_af; //Todo: 4bits
uint16_t rx_pin; //Todo: 4bits
};
static struct gd32_uart uarts =
{
USART0, // uart peripheral index
USART0_IRQn, // uart iqrn
RCU_USART0, RCU_GPIOA, RCU_GPIOA, // periph clock, tx gpio clock, rt gpio clock
GPIOA, GPIO_AF_7, GPIO_PIN_9, // tx port, tx alternate, tx pin
GPIOA, 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进行串口的初始化。
之后可以创建两个线程,一个发送一个接收,通过操作系统提供的事件来进行同步。发送线程时刻等待发送事件,一旦接收到发送事件则开始发送数据。接收线程时刻等待接收事件,一旦接收到接收事件则从串口中读数据填入缓存,以备后续处理。