【GD32F470紫藤派使用手册】第十讲 USART-中断串口收发实验

10.1 实验内容

通过本实验主要学习以下内容:

  • 使用中断进行串口收发

10.2 实验原理

10.2.1 串口寄存器介绍

串口有几个非常重要的寄存器需要读者理解。

数据寄存器(USART_DATA)

该寄存器虽然只有一个,但内部是映射为发送和接受两个寄存器。

发送时,除了发送数据寄存器,还有一个移位寄存器,当数据写入数据寄存器中,移位寄存器空闲的情况下,数据从数据寄存器中转移到移位寄存器,移位寄存器按照低bit——高bit的顺序将数据移位到IO口上。

接收时,接收到的数据保存在数据寄存器中,CPU或DMA可以从该寄存器中读接收到的数据。

状态寄存器0(USART_STAT0  )

 我们需要特别理解TBE、TC、RBNE、IDLE、OREE这几位。

1.TBE(发送空):这个位置“1”表示现在可以往数据寄存器中写数据了,当移位寄存器空闲时,写入到数据寄存器中的数据则会转移到移位寄存器中,串口开始对外发送数据;

2.TC(发送完成):发送数据时,当数据寄存器和移位寄存器都为空时,表示所有的数据都已经完成了,则TC置“1”,所以当连续发数据时,最后一个字节从移位寄存器中发送完,TC才会置起。

3.RBNE(接受非空):当串口接受到一个字节数据,RBNE置“1”,此时CPU可以去数据寄存器中取数据,当使用了DMA接受,DMA自动将数据寄存器中数据搬走,当数据寄存器数据被读走/搬走,RBNE位自动清“0”;

4.IDLE(空闲):该标志位用于检测接受空闲,当串口接受最后一个字节后,再往后一个字节时间内,没有接受到新的数据,则该位置“1”;

IDLE一般用于串口DMA接受中,DMA接受中,MCU无法知道发送方的数据个数,所以可以通过判断IDLE位(或IDLE中断)来判断发送方一帧数据发送结束了。

 5.OREE(溢出错误):当RBNE置位的情况,又接收到一个字节数据,则OREE位置“1”。

以上就是串口寄存器的介绍。本实验就是使用TBE中断和RBNE中断来实现中断收发数据,实验原理是RBNE中断用来接受数据,IDLE中断用于判断发送方数据结束,TBE中断用于发送数据。

10.3 硬件设计

本实验使用P1接口的PA9和PA10实现串口功能,硬件设计请见上一章。

10.4 代码解析

10.4.1 串口中断发送函数

在driver_uart.c中定义了串口中断发送函数:

C
Drv_Err driver_uart_int_transmit(typdef_uart_struct *uartx,uint8_t *pbuff,uint16_t length)
{
    uint64_t timeout = driver_tick;    
    while(uartx->uart_control.Com_Flag.Bits.SendState==1){
        if((timeout+UART_TIMEOUT_MS) <= driver_tick) {              
            uartx->uart_control.Com_Flag.Bits.SendState=0;
            return DRV_ERROR;        
        }
    }
    
    uartx->uart_control.Com_Flag.Bits.SendSucess=0;
    uartx->uart_control.Com_Flag.Bits.SendState=1;  
    uartx->uart_control.p_Send=pbuff;
    uartx->uart_control.SendSize=length;
    uartx->uart_control.SendCount=0;
    
    usart_flag_clear(uartx->uart_x,USART_FLAG_TC);
    usart_interrupt_enable(uartx->uart_x,USART_INT_TBE);
    
    return DRV_SUCCESS;    
}

 10.4.2 串口中断接受函数

在driver_uart.c中定义了串口中断接受函数:

C
Drv_Err driver_uart_int_receive(typdef_uart_struct *uartx,uint8_t *pbuff,uint16_t length)
{    
    uint64_t timeout = driver_tick;     
    while(uartx->uart_control.Com_Flag.Bits.RecState==1){
        if((timeout+UART_TIMEOUT_MS) <= driver_tick) {              
            uartx->uart_control.Com_Flag.Bits.RecState=0;
            return DRV_ERROR;        
        }
    }

    if(usart_flag_get(uartx->uart_x,USART_FLAG_ORERR))
    {
        usart_flag_clear(uartx->uart_x,USART_FLAG_ORERR);
        USART_STAT0(uartx->uart_x);
        USART_DATA(uartx->uart_x);        
    }    
    
    uartx->uart_control.Com_Flag.Bits.RecSuccess=0;
    uartx->uart_control.Com_Flag.Bits.RecState=1;
    uartx->uart_control.p_Rec=pbuff;
    uartx->uart_control.RecSize=length;
    uartx->uart_control.RecCount=0;    

    usart_flag_clear(uartx->uart_x,USART_FLAG_IDLE);
    USART_STAT0(uartx->uart_x);
    USART_DATA(uartx->uart_x);  
    
    usart_interrupt_enable(uartx->uart_x,USART_INT_RBNE);
    usart_interrupt_enable(uartx->uart_x,USART_INT_IDLE);
    
    return DRV_SUCCESS;    
}

10.4.3 main函数实现

以下为main函数代码:

C
int main(void)
{
    //延时、共用驱动部分初始化    
    driver_init();

    //初始化UART为中断模式,注册接受完成(IDLE)回调函数
    BOARD_UART.uart_mode_tx=MODE_INT;
    BOARD_UART.uart_mode_rx=MODE_INT;
    BOARD_UART.uart_idle_callback=user_receive_complete_callback;       
    bsp_uart_init(&BOARD_UART);

    bsp_led_init(&LED2);     
    bsp_led_init(&LED1);
    bsp_led_on(&LED2);
    bsp_led_off(&LED1);    

    //使能UART中断    
    nvic_irq_enable(USART0_IRQn,2,0);

    delay_ms(100);
    printf_log("uart interrupt mode sends and receives loopback packets of indefinite length.\r\n");

    //启动UART中断接受,最长100byte
    driver_uart_int_receive(&BOARD_UART,uart_rec_buff,100);
    
        while (1)
        {
        //查询到接受完成回调函数标志
        if(uart_receive_complete_flag==SET)
        {
            uart_receive_complete_flag=RESET;
            
            //启动中断方式发送刚接受到的数据
            driver_uart_int_transmit(&BOARD_UART,uart_send_buff,uart_receive_count);  
            bsp_lcd_printf("%s",uart_send_buff);            
        }
        }
}

本例程main函数首先进行了延时函数初始化,再初始化UART为中断模式,接着配置串口BOARD_UART,开启串口中断NVIC,这里使用到了IDLE中断,TBE中断和RBNE中断,然后配置串口D中断接受,最长100个字节,所以我们可以给串口发送100个字节以下长度的数据。在while(1)循环中循环查询uart_receive_complete_flag标志位,当该标志位为“SET”时,表示IDLE中断被触发,一帧数据接受完,最后将接收到的帧数据通过中断发送方式原封不动发送到串口上。

10.4.4 中断函数

在bsp_uart.c中定义了串口中断处理函数

C
void USART0_IRQHandler(void)
{
    driver_uart_int_handler(&BOARD_UART);
}

 在driver_uart.c中定义了driver_uart_int_handler函数:

C
Drv_Err driver_uart_int_handler(typdef_uart_struct *uartx)
{   
    Drv_Err uart_state=DRV_SUCCESS;    
    if(usart_interrupt_flag_get(uartx->uart_x,USART_INT_FLAG_RBNE)!=RESET)
    {
        if(uartx->uart_control.RecCount < uartx->uart_control.RecSize){
            uartx->uart_control.p_Rec[uartx->uart_control.RecCount]=usart_data_receive(uartx->uart_x);
            uartx->uart_control.RecCount++;            
        }
        else{
            usart_data_receive(uartx->uart_x);
            uart_state=DRV_ERROR;
            //err 溢出
        }
        if(uartx->uart_rbne_callback!=NULL){
            uartx->uart_rbne_callback(uartx);
        }        
        //callback
        if(uartx->uart_control.RecCount == uartx->uart_control.RecSize){
            uartx->uart_control.Com_Flag.Bits.RecSuccess=1;            
            uartx->uart_control.Com_Flag.Bits.RecState=0;
            uartx->uart_control.RecCount=0;            
        }        
    }       
    if(usart_interrupt_flag_get(uartx->uart_x,USART_INT_FLAG_IDLE)!=RESET)
    {
        usart_interrupt_flag_clear(uartx->uart_x,USART_INT_FLAG_IDLE);
        USART_STAT0(uartx->uart_x);
        USART_DATA(uartx->uart_x);
        
        if( (uartx->uart_mode_rx==MODE_INT && uartx->uart_control.RecCount>0) \
            ||(uartx->uart_mode_rx==MODE_DMA && dma_transfer_number_get(uartx->uart_rx_dma->dmax,uartx->uart_rx_dma->dma_chx)!=uartx->uart_control.RecSize))
        {
            uartx->uart_control.Com_Flag.Bits.RecSuccess=1;
            uartx->uart_control.Com_Flag.Bits.RecState=0;              
            
            if(uartx->uart_mode_rx==MODE_DMA){
                uartx->uart_control.RecCount=uartx->uart_control.RecSize-dma_transfer_number_get(uartx->uart_rx_dma->dmax,uartx->uart_rx_dma->dma_chx);            
            }
            //callback            
            if(uartx->uart_idle_callback!=NULL){
                uartx->uart_idle_callback(uartx);
            }                                
        }        
            
    }
    
    if(usart_interrupt_flag_get(uartx->uart_x,USART_INT_FLAG_TBE)!=RESET)
    {
        usart_data_transmit(uartx->uart_x,uartx->uart_control.p_Send[uartx->uart_control.SendCount]);
        uartx->uart_control.SendCount++;
        
        if(uartx->uart_tbe_callback!=NULL){
            uartx->uart_tbe_callback(uartx);
        }
        
        if(uartx->uart_control.SendCount >= uartx->uart_control.SendSize)
        {
            uartx->uart_control.SendCount=0;            
            usart_interrupt_disable(uartx->uart_x, USART_INT_TBE);
            usart_interrupt_enable(uartx->uart_x, USART_INT_TC);
        }
    }

    if(usart_interrupt_flag_get(uartx->uart_x,USART_INT_FLAG_TC)!=RESET)
    {
        usart_interrupt_disable(uartx->uart_x, USART_INT_TC);          
        usart_flag_clear(uartx->uart_x,USART_FLAG_TC);  

        if( !(uartx->uart_mode_rx==MODE_DMA && dma_transfer_number_get(uartx->uart_tx_dma->dmax,uartx->uart_tx_dma->dma_chx)!=0) )
        {    
            uartx->uart_control.Com_Flag.Bits.SendSucess=1;            
            uartx->uart_control.Com_Flag.Bits.SendState=0;
            
            if(uartx->uart_tc_callback!=NULL){
                uartx->uart_tc_callback(uartx);
            }         
            
            uartx->uart_control.SendCount=0;
        }
    }  

    if(usart_flag_get(uartx->uart_x,USART_FLAG_ORERR)==SET)
    {
        usart_flag_clear(uartx->uart_x,USART_FLAG_ORERR);        
        USART_STAT0(uartx->uart_x);
        USART_DATA(uartx->uart_x);
        uart_state=DRV_ERROR;
    }

    return uart_state;     

}

10.5 实验结果

使用串口调试助手发送一帧数据到MCU,MCU会将这帧数据回发到串口调试助手中。

由聚沃科技原创,来源于GD32F470紫藤派 - 文章中心 - 苏州聚沃电子科技有限公司

GD32MCU技术交流群:859440462

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/700213.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

供水设备数据采集

随着城市化进程的加快&#xff0c;供水系统作为城市基础设施的重要组成部分&#xff0c;其运行效率和稳定性直接关系到市民的日常生活。在这个信息化、智能化的时代&#xff0c;如何利用先进技术提升供水系统的管理水平&#xff0c;成为了摆在我们面前的重要课题。HiWoo Cloud平…

ROS2+TurtleBot3+Cartographer+Nav2实现slam建图和导航

0 引言 入门机器人最常见的应用就是slam建图和导航&#xff0c;本文将详细介绍这一流程&#xff0c; 便于初学这快速上手。 首先对需要用到的软件包就行简单介绍。 turtlebot3: 是一个小型的&#xff0c;基于ros的移动机器人。 学习机器人的很多示例程序都是基于turtlebot3。 …

AIGC数字人视频创作平台,赋能企业常态化制作数字内容营销

随着数字人技术不断发展&#xff0c;AIGC、元宇宙等相关产业迅速发展&#xff0c;企业通过3D虚拟数字人定制&#xff0c;打造出专属的数字人作为企业与用户沟通的新桥梁。 作为3D、AI数字人技术服务商及方案提供商&#xff0c;广州虚拟动力一直致力于为各领域企业通过3D虚拟数字…

Fabric实现多GPU运行

官方的将pytorch转换为fabric简单分为五个步骤&#xff1a; 步骤 1&#xff1a; 在训练代码的开头创建 Fabric 对象 from lightning.fabric import Fabricfabric Fabric() 步骤 2&#xff1a; 如果打算使用多个设备&#xff08;例如多 GPU&#xff09;&#xff0c;就调用…

搞什么副业可以月入过万?

月入过万的副业不是一件容易的事情&#xff0c;它需要你付出很多努力和时间。以下是一些可能能够实现月入过万的副业 1. 自媒体运营 通过开设自己的公众号、博客或YouTube频道&#xff0c;积累一定的粉丝和流量&#xff0c;然后通过广告、赞助、商品销售等方式赚取收入。 2. …

Python入门系列-02 pip的安装

目录 一、pip介绍二、pip安装检查三、pip安装 一、pip介绍 pip 是 Python 包管理工具&#xff0c;该工具提供了对Python 包的查找、下载、安装、卸载的功能。 二、pip安装检查 你可以通过以下命令来判断是否已安装。 pip --version # Python2.x 版本命令 pip3 --versio…

Redis分布式缓存

分布式缓存 引入&#xff1a; 一&#xff1a;持久化&#xff1a; 1.1.RDB持久化&#xff1a; 1.2.AOF文件&#xff1a; 记得关闭RDB&#xff0c;开启AOF。 注意&#xff0c;AOF默认是详细的记录每一条命令&#xff0c;即使是对同一个key的多次修改&#xff0c;RDB只会记录最…

云端的艺术革命:云渲染如何重塑动画与视觉特效产业

在 2019 年&#xff0c;乔恩费儒&#xff08;Jon Favreau&#xff09;决定重拍迪士尼的经典电影《狮子王》。他的创新构想是以真实动物为模型&#xff0c;在非洲草原上拍摄&#xff0c;由真实动物“出演”的辛巴和其他角色&#xff0c;随后通过配音赋予它们生命。 为了实现这一…

emp.dll文件丢失荒野大镖客,怎么快速修复emp.dll

缺失或损坏的 DLL 文件是会导致系统或软件故障的&#xff0c;DLL&#xff08;动态链接库&#xff09;文件是 Windows 操作系统中至关重要的一部分&#xff0c;它们允许多个程序共享代码和资源&#xff0c;从而减少内存占用和增强系统性能。然而&#xff0c;当EMP.dll文件丢失或…

基于springboot+vue+Mysql的在线答疑系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

The Quantcast File System——论文泛读

VLDB 2013 Paper 分布式元数据论文阅读笔记整理 问题 在2013年之前&#xff0c;由于网络链路带宽有限&#xff0c;数据在集群中移动速度慢&#xff0c;因此Hadoop尽量将数据留在原来的位置&#xff0c;并将处理代码发送给它。随着网络链路的发展&#xff0c;可以之前更高的数…

使用Nginx对网站资源进行加密访问并限制访问IP

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 大家在工作中有没有遇到过这样的需求&#xff0c;新上的网站部署到生产服务器上&#xff0c;但是还没公开&#xff0c;只允许个别高层领导看。 思来想去&#xff0c;我想到了一个简单的方法&#xff0c;通过Nginx对网站…