开篇分割线,我们在上一篇讲过了uart相关的ops函数集,还有个更重要的就是uart中断,发送你可以不用,但是接收什么时候来数据,数据能不能及时的进行处理,这都离不开中断的作用,所以一般应用中中断接收是必选项。
我们在写uart驱动的时候,就需要考虑如何将中断事件(有接收数据到达)通知给uart设备驱动框架(也就是serial层),怎么发通知呢,当然是调用uart设备驱动框架提供的rt_hw_serial_isr函数,设备框架才能知道中断到底发生了什么事。函数原型如下:
serial:串口设备句柄,来自于哪个串口设备的中断,对应的句柄就是谁。
event:中断事件类型。
接下来看一下stm32——uart设备中断处理的真实操作:
/*uart_isr的主要用途:调用框架层rt_hw_serial_isr,通知上层中断已经发生*/
static void uart_isr(struct rt_serial_device *serial)
{
struct stm32_uart *uart;
#ifdef RT_SERIAL_USING_DMA
rt_size_t recv_total_index, recv_len;
rt_base_t level;
#endif
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct stm32_uart, serial);
/* UART in mode Receiver -------------------------------------------------*/
if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET))
{
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
}
//......这里还有好多处理 例如DMA中断 发送中断等不是重点,不展开了。
}
这段代码是uart_isr 的一部分专门处理接收中断触发的数据,作用很简单,如果读数据寄存器不为空,且RXNE为中断使能状态,则调用rt_hw_serial_isr触发读数据操作,关于该函数的响应事件类型如下:
#define RT_SERIAL_EVENT_RX_IND 0x01 /* 接收一个字节数据*/
#define RT_SERIAL_EVENT_TX_DONE 0x02 /* 一个字节数据发送完成 */
#define RT_SERIAL_EVENT_RX_DMADONE 0x03 /* DMA接收完成 */
#define RT_SERIAL_EVENT_TX_DMADONE 0x04 /* DMA发送完成*/
接下来我们看下框架提供的代码都做了哪些工作?
/* ISR for serial interrupt */
void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
{
switch (event & 0xff)
{
case RT_SERIAL_EVENT_RX_IND:
{
int ch = -1;
rt_base_t level;
struct rt_serial_rx_fifo* rx_fifo;
/* interrupt mode receive */
rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
RT_ASSERT(rx_fifo != RT_NULL);
while (1)
{
ch = serial->ops->getc(serial);
if (ch == -1) break;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rx_fifo->buffer[rx_fifo->put_index] = ch;
rx_fifo->put_index += 1;
if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0;
/* if the next position is read index, discard this 'read char' */
if (rx_fifo->put_index == rx_fifo->get_index)
{
rx_fifo->get_index += 1;
rx_fifo->is_full = RT_TRUE;
if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;
_serial_check_buffer_size();
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/* invoke callback */
if (serial->parent.rx_indicate != RT_NULL)
{
rt_size_t rx_length;
/* get rx length */
level = rt_hw_interrupt_disable();
rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index):
(serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index));
rt_hw_interrupt_enable(level);
if (rx_length)
{
serial->parent.rx_indicate(&serial->parent, rx_length);
}
}
break;
}
//.......这部分处理其它的事件响应
}
}
当触发事件为接收一个字符是,那么就开启读取一个字符,在进行数据存储时防止数据出现异常,关闭硬件中断,写入成功后再次开启终端,再中断关闭时,中断标记位是有效的,也就是说连续来的数据可能已经写入到了DR寄存器中,所以这个时候就可以连续读DR寄存器,直到ch返回-1。这个时候代表这当前已无数据,判断当前回调函数是否为空,不为空则更新缓冲区新存入数据长度(这个过程依旧是关中断执行)。得到正确的数据长度后,调用回调函数读取数据,到这里关于uart设备驱动的全部内容就结束了,当然关于发送中断、DMA中断部分内容并没有深入去讲解,感兴趣的小伙伴可以自己尝试分析分析该部分代码的实现逻辑。
接下来是我们更感兴趣的serial层数据的处理,筒子们下期再见~!