首页 > 其他分享 >【单片机/嵌入式】【梁山派】学习日志08:串口通信USART

【单片机/嵌入式】【梁山派】学习日志08:串口通信USART

时间:2022-11-06 20:58:00浏览次数:54  
标签:USART usart 08 BSP 参数 串口 GPIO

串口

一、串口通信原理

1.1串口基础知识

1.1.1串口介绍

串口是指外设和处理器之间通过数据信号线、地线和控制线等,按位进行传输数据的一种通讯方式。尽管传输速度比并行传输低。但串口可以在使用一根线发送数据的同时用另一根线接收数据。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位,这些参数在两个通信端口之间必须一致。

1.1.2串口通信参数介绍

波特率: 衡量通信速度的参数,它表示每秒钟传送的bit的个数。

数据位: 衡量通信中实际数据位的参数,表示一个信息包里包含的数据位的个数。

停止位: 用于表示单个信息包的最后位,典型值为1、1.5和2位。由于数据是在传输线上传输的,每个设备都有自己的时钟,很有可能在通信过程中出现不同步,停止位不仅仅表示传输的结束,还能提供校正时钟同步的机会。停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率也越慢。

奇偶检验位: 表示一种简单的检查错误的方式。

1.1.3串口工作模式

串口可以工作在单工、半双工和全双工模式下。

单工:在通信的任意时刻,信息只能由A传到B。

半双工:在通信的任意时刻,信息即可由A传到B,又能由B传到A,但同时只能有一个方向上的传输存在。

全双工:在通信的任意时刻,通信线路上存在A到B和B到A的双向信号传输。

1.1.4串口通信协议

串口在进行通信的时候会按照数据包的形式进行发送,帧格式如图所示。

串口通信是一位一位地传输,每传输一个字符总是以起始位开始,以停止位结束,字符之间没有固定的时间间隔要求。

每一个字符的前面都有一位起始位(低电平),后面由7位数据位组成,接着是一位校验位,最后是停止位。

停止位后面是不定长的空闲位,停止位和空闲位都规定为高电平。

1.2串口接口原理图

在开发板上默认使用的串口是串口1,有两根数据线,也就是USART1_TX和USART1_RX。串口引脚和下载引脚连接在同一个端子上,插上DAPLink就可以进行下载和串口调试。关于串口原理图如图所示。

1.3串口驱动流程

拿到开发板,把DAPLink连接到开发板的端子上,打开串口调试助手(在资料包/02开发工具/串口调试工具/sscom5.13.1),然后会检测到一个串口,如图所示。

编写代码,先要配置串口使能,配置波特率、停止位、校验位等参数。然后调用串口发送函数即可发送数据。如使用重定向还需要编写重定向函数,使用printf即可打印输出。

二、串口打印信息

2.1串口配置

一般我们使用串口,都需要有以下几个步骤。

l  开启时钟(包括串口时钟和GPIO时钟)

l  配置GPIO复用模式

l  配置GPIO的模式

l  配置GPIO的输出

l  配置串口(配置一些参数)

l  使能串口(串口使能和发送使能)

2.1.1开启时钟

使用串口0的话就是PA9和PA10引脚。

可在芯片手册中查到,如下:

那第一步就是先开启端口A的时钟,在库函数点灯那一章节给大家介绍了使能时钟的函数rcu_periph_clock_enable,只需要传入对应的参数即可。使能端口A的时钟就把RCU_GPIOA当做参数传入。第二步就是开启串口的时钟,把对应的串口0的时钟RCU_USART0传入即可。为了方便后续修改,把端口A的时钟和串口0的时钟用宏定义定义,如下:

#define BSP_USART_RCU              RCU_USART0
#define BSP_USART_TX_RCU          RCU_GPIOA
#define BSP_USART_RX_RCU          RCU_GPIOA

然后对应的使能时钟的代码就是:

rcu_periph_clock_enable(BSP_USART_RCU); // 开启串口时钟
rcu_periph_clock_enable(BSP_USART_TX_RCU); // 开启端口时钟
rcu_periph_clock_enable(BSP_USART_RX_RCU); // 开启端口时钟

2.1.2配置GPIO复用模式

GD32的引脚是可以有复用功能的,就是说单个引脚可有很多个功能,默认的功能一般都是作为GPIO使用。在gd32f4xx_gpio.h中可以查找到设置复用的函数void gpio_af_set(uint32_t gpio_periph, uint32_t alt_func_num, uint32_t pin);

这个函数有三个参数,第一个参数就是要配置的引脚端口,第二个参数就是要复用的功能,第三个参数就是要配置的引脚。

固件库使用指南338页:

第一个参数和第三个参数我们知道分别为GPIOA,GPIO_PIN_9,GPIOA,GPIO_PIN_10。关于第二个参数可以到芯片数据手册的第46页进行查找,如图所示。

 

从图可以看到PA9对应的USART0_TX的功能复用为AF7,PA10对应的USART0_RX的功能复用为AF7,那第二个参数我们也知道了。接下来就可以开启PA9,PA10的复用功能了。代码编写如下。首先定义一下端口和复用功能的宏定义。

#define BSP_USART_TX_PORT      GPIOA
#define BSP_USART_TX_PIN        GPIO_PIN_9
#define BSP_USART_RX_PORT       GPIOA
#define BSP_USART_RX_PIN        GPIO_PIN_10
#define BSP_USART_AF        GPIO_AF_7  // 串口是引脚复用功能7
/* 配置复用功能 */
gpio_af_set(BSP_USART_TX_PORT,BSP_USART_AF,BSP_USART_TX_PIN);
gpio_af_set(BSP_USART_RX_PORT,BSP_USART_AF,BSP_USART_RX_PIN);

通过上面两句就可以把PA9,PA10设置为串口功能了。

2.1.3配置GPIO的模式

配置GPIO的模式还是使用gpio_mode_set这个函数,不同的是第二个参数要配置为复用功能而不是输出功能,第三个参数要配置为上拉。

转化为代码如下。

/* 配置TX为复用模式 上拉模式 */
gpio_mode_set(BSP_USART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_USART_TX_PIN);
/* 配置RX为复用模式 上拉模式 */
gpio_mode_set(BSP_USART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_USART_RX_PIN);

根据官方给的代码例程,需要GPIO引脚需要上拉:

用户手册497页,引脚描述如下:

 

2.1.4配置GPIO的输出

配置GPIO的输出也还是用gpio_output_options_set这个函数,这个和之前库函数点灯配置的一样,修改一下引脚就可以直接套用。

/* 配置TX为推挽输出 50MHZ */
gpio_output_options_set(BSP_USART_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_USART_TX_PIN);
/* 配置RX为推挽输出 50MHZ */
gpio_output_options_set(BSP_USART_RX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, BSP_USART_RX_PIN);

当我们配置为输出模式的时候,可以有两种输出模式选择,一种是推挽输出模式,一种是开漏输出模式。由于开漏需要外接上拉电阻才可以输出高电平,这里并不适合,所以需要设置为推挽输出。

2.1.5配置串口

和之前库函数点灯不同的是,使用串口还需要进行串口的配置。前面只是对GPIO的引脚做了配置,然后就可以操作GPIO进行输入输出,但是要使用串口,还需要设置串口的一些参数,后面使用其它的资源也是如此。

在前一章节的串口原理介绍中讲到串口有几个必要的参数,分别是波特率,校验位,数据位,停止位。在代码里也就是要设置这几个参数。

(1)复位

在使用一个外设之前,可以先复位一下,防止一些不必要的事情发生。在对应的外设库文件中一般都会有这个函数。在gd32f4xx_uart.h头文件中有。这个函数复位串口。有一个参数,就是要复位的串口:void usart_deinit(uint32_t usart_periph);

(2)波特率

这个函数设置波特率。有两个参数,分别为要设置的串口和要设置的波特率:void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);

(3)校验功能

这个函数配置校验功能。有两个参数,第一个就是要配置的串口,第二个就是设置校验方式:void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);关于校验方式的选项如图所示。

从图可以看到,校验方式可以选择奇校验、偶检验和无校验。一般配置为无校验即可。

(4)数据为长度

这个函数设置数据位的长度。有两个参数,第一个参数就是要配置的串口,第二个参数就是要设置的数据位的长度:void usart_word_length_set(uint32_t usart_periph, uint32_t wlen);关于数据位长度的选项如图所示。

从图可以知道关于数据长度有两个选项,一般选择8位即可。

(5)停止位

这个函数设置停止位的位数。有两个参数,第一个参数就是要配置的串口,第二个参数就是要设置的停止位的位数,关于停止位的位数的选项如图所示。

停止位一般选择1位即可。

根据以上所学,编写串口配置代码如下:

/* 串口配置*/   
usart_deinit(BSP_USART); // 复位串口   
usart_baudrate_set(BSP_USART,dwbaud_rate); // 设置波特率   
usart_parity_config(BSP_USART,USART_PM_NONE); // 没有校验位   
usart_word_length_set(BSP_USART,USART_WL_8BIT); // 8位数据位   
usart_stop_bit_set(BSP_USART,USART_STB_1BIT); // 1位停止位
这里要注意一下BSP_USART是一个宏定义
#define BSP_USART    USART0

dwbaud_rate是函数里的一个形参,为了方便修改串口的波特率,函数实体为void usart_gpio_config(uint32_t dwbaud_rate),在使用的时候,可以直接传入对应的波特率,例如配置波特率为115200,转化为代码可写为

usart_gpio_config(115200); // 串口配置

如还需要配置其它的参数请查找对应的函数调用即可。

2.1.6使能串口

串口配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。值得注意的是发送和接收也需要分别去使能,串口使能相当于一个总开关,发送和接收相当于分别控制的开关。要使用串口,首先要打开总开关。

这个函数使能串口,有一个参数,就是要使能的串口:void usart_enable(uint32_t usart_periph);

如果要发送数据的话还需要使能发送功能:void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);这个函数配置串口发送。有两个参数,第二个参数对应的选项如图所示。

从图中可以看到,如果使能串口发送就配置为USART_TRANSMIT_ENABLE。

要接收数据的话还需要使能接收功能。这个函数配置串口接收。void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);有一个参数,对应的选项如图所示。

从图中可以看到,如果使能串口接收就配置为USART_RECEIVE_ENABLE。

我们只使用串口发送功能,所以只需要配置串口使能和串口发送使能即可。转化为代码为

usart_transmit_config(BSP_USART,USART_TRANSMIT_ENABLE); // 使能串口发送

usart_enable(BSP_USART); // 使能串口

2.2串口发送数据

配置好串口之后,下一步的操作就是要发送数据。

void usart_data_transmit(uint32_t usart_periph, uint32_t data); 这个函数可以发送数据,有两个参数,第一个参数是要使用的串口,第二个参数是要发送的数据,不过需要注意的是这个函数一次只能发送一个字节。要保证串口稳定的传输,就需要在发送完一个字节之后再发送下一个字节。需要去检测数据发送完成。

FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag); 这个函数是获取状态寄存器的标志。有两个参数,第一个参数是要使用的串口,第二个参数是要获取的状态位,状态位选项为图所示(只截取一部分)。

可以获取发送缓冲区的标志位(缓冲区空标志TBE),看当前是否还有数据,如果有数据,将等待,如果没有数据就可以继续发送。

从图可以了解到当将要发送的数据写入USART_DATA时,此位被清0,当数据发送完成之后,此位置1。所以当检测到此位为1时就表明当前数据缓冲区为空,可以继续发送数据。

关于串口发送数据可以封装为一个函数如下

void usart_send_data(uint8_t ucch)
{
    usart_data_transmit(BSP_USART, (uint8_t)ucch); 
    while(RESET == usart_flag_get(BSP_USART, USART_FLAG_TBE)); // 等待发送数据缓冲区标志置位
}

这样一个字符一个字符的打印输出是不是很麻烦,没关系,我们可以再进行封装一层,一次发送一个字符串。

void usart_send_String(uint8_t *ucstr)
{  
      while(ucstr && *ucstr)  // 地址为空或者值为空跳出  
      {    
        usart_send_data(*ucstr++);   
      }
}

2.3串口重定向

上面封装的发送字符串的方式打印信息看似方便,但如果我们想要打印数字,小数,该怎么打印呢?大家是否习惯使用了printf这个函数,可以通过%d,%f打印整形和小数,这一小节就教大家怎么把串口重定向到printf函数。

2.3.1串口重定向介绍

C语言中的printf函数默认输出设备是显示器,如果要在串口显示,必须重新定义标准库函数里调用的与输出设备相关的函数。需要注意的是,在keil中使用printf一定要勾选“微库”选项。

2.3.2 printf重定向

首先c语言的printf函数中不断循环调用fputc函数,所以需要重写fputc函数,这个函数的功能就是打印输出一个字符,这不正和我们编写的usart_send_data函数功能一样。fputc函数可写为

int fputc(int ch, FILE *f)
{    
    usart_data_transmit(BSP_USART, (uint8_t)ch);  
    while(RESET == usart_flag_get(BSP_USART, USART_FLAG_TBE)); // 等待发送数据缓冲区标志置位 
    return ch;
}

编写好fputc函数之后就可以使用printf函数输出信息了。只要将fputc函数放入主函数,或者其他可以包括的.c文件中即可。

2.4举一反三

通过上面的一系列步骤,我们可以用串口0发送数据,那如果用串口1发送数据,该如何操作呢?

还记得我们前面配置的宏定义吗,宏定义的好处就是方便移植。把串口0改为串口1,函数主体都不用修改,只需要修改宏定义即可。

串口1的引脚接口为PA2和PA3,所以串口1的宏定义如图所示。

#define BSP_USART_RCU              RCU_USART1
#define BSP_USART_TX_RCU          RCU_GPIOA
#define BSP_USART_TX_PORT              GPIOA
#define BSP_USART_TX_PIN          GPIO_PIN_2
#define BSP_USART_RX_RCU          RCU_GPIOA
#define BSP_USART_RX_PORT              GPIOA
#define BSP_USART_RX_PIN          GPIO_PIN_3
#define BSP_USART_AF                GPIO_AF_7  // 串口是引脚复用功能7
#define BSP_USART                USART1

代码中只需要将串口0的宏定义修改为串口1的宏定义就可以使用串口1发送信息了。

2.5实验现象

 

标签:USART,usart,08,BSP,参数,串口,GPIO
From: https://www.cnblogs.com/steven913/p/16863901.html

相关文章

  • 2022-2023-1 20221408《计算机基础与程序设计》第十周学习总结
    第十周学习总结作业信息这个作业属于哪个课程:https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP这个作业的要求在哪里:https://www.cnblogs.com/rocedu/p/9577842......
  • 000008 PHP 递增递减运算符
    <?phpheader('Content-Type:text/html;charset=utf-8');include'./assets/php/head.php';//递增/递减运算符/***递增/递减运算符例子名称效果*++$a前加$......
  • 串口接收方式
    最近学到stm32的串口发送,一直不理解他是如何发送和接收每个字节的,以及汉字是如何进行传输的     根据对usart的配置可以知道,串口一次传输8个位。而我们知道一个......
  • 升级node最新版本18.x .Error: error:0308010C
    Buildingforproduction...Error:error:0308010C:digitalenveloperoutines::unsupported先执行以下命令:windows:setNODE_OPTIONS=--openssl-legacy-providerlinux......
  • 20201208史逸霏第十二章学习笔记
    第十二章读书笔记:块设备I/O和缓冲区管理12.1块设备I/O缓冲区I/O缓冲的基本原理文件系统使用一系列I/O缓冲区作为块设备的缓存内存。当进程试图读取(dev,blk)标识的......
  • 51单片机串口通信
    51单片机串口通信一、串口通信介绍随着单片机系统的广泛应用和计算机网络技术的普及,单片机与单片机或其他网络主机的通信愈来愈重要。在WIFI、蓝牙、GPS、GSM/GPRS等应用......
  • 「题解报告」[POI2008]PER-Permutation
    「题解报告」[POI2008]PER-Permutation点击查看目录目录「题解报告」[POI2008]PER-Permutation思路代码不理解哪里难了,学过扩卢并且推一下式子基本就是两眼切吧。......
  • 动力节点——day08
    什么是集合,有什么用?数组其实就是一个集合,集合实际上就是一个容器,可以用来容纳其他数据类型集合为什么说在开发中使用最多?集合是一个容器,是一个载体,可以一次容纳多......
  • CH58X/CH57X/V208的Broadcaster(广播者)例程讲解
    在对ble进行应用的时候,每个用户的需求可能不尽相同。这里着重介绍从机Broadcaster例程,只广播不连接。使用该例程时可以在手机使用APP上对Broadcaster进行调试。安卓端在......
  • P1083 [NOIP2012 提高组] 借教室
    #include<iostream>usingnamespacestd;constintN=1e6+1;intn,m;inta[N];structnode{inttag,sum;node(){tag=sum=0;}v......