该部分不写初始化部分,直接从接收到代码后开始分析。
使用单片机GD32F303RET6,同时使用该单片机的库进行分析。
要解决的疑问:
Q1:在接收数据接收一半的时候,应用代码读取数据会怎么样?
在读取的时候,会导致前面一半的数据被读取出来,但是后面的数据会继续接收,在下一次读取数据的时候读取出来。
//代码接收,在isr代码中进行数据处理,同时进入中断标记。
void USART0_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
uart_isr(&serial0);
/* leave interrupt */
rt_interrupt_leave();
}
static void uart_isr(struct rt_serial_device *serial)
{
//user_data代码中保存的是设备的结构体的指针
struct gd32_uart *uart = (struct gd32_uart *) serial->parent.user_data;
RT_ASSERT(uart != RT_NULL);
/* 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 rt_hw_serial_isr(struct rt_serial_device *serial, int event)函数里面通过 ch = serial->ops->getc(serial);获取接收到的数据。getc在初始化的时候,初始为了下面的这个函数。
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
};
uarts[i].serial->ops = &gd32_uart_ops;
在void rt_hw_serial_isr(struct rt_serial_device *serial, int event)函数里面,通过下面代码将接收到的数据写入串口接收缓冲区。
/* 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);
在上面的那个函数中,下面的这段代码实现了,回调函数。serial->parent.rx_indicate这个是回调函数的指针。
/* 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);
}
}
原作者:于周斐
更多回帖