我们在上一章节已经了解了串口的概念,并且介绍了开发板上的USB转串口芯片的连接,接下来我们开始进行串口信息收发实验。即能够通过串口将数据发送到电脑端显示,也能够接收从电脑端发送过来的数据进行显示;
配置流程
一般我们使用串口,都需要有以下几个步骤。
- 开启时钟(包括串口时钟和GPIO时钟)
- 配置GPIO复用模式
- 配置GPIO的模式
- 配置GPIO的输出
- 配置串口(配置一些参数)
- 使能串口(串口使能和发送使能)
开启时钟
使用串口0的话有很多个引脚都支持串口0,这里我们选择使用PA8和PB15引脚,它们支持串口0。
那第一步就是先开启端口A和端口B的时钟,在库函数点灯那一章节给大家介绍了使能时钟的函数rcu_periph_clock_enable
,只需要传入对应的参数即可。使能端口A的时钟就把RCU_GPIOA
当做参数传入,端口B同理。第二步就是开启串口的时钟,把对应的串口0的时钟RCU_USART0传入即可。为了方便后续修改,把端口AB的时钟和串口0的时钟用宏定义定义,如下:
#define BSP_USART_RCU RCU_USART0
#define BSP_USART_TX_RCU RCU_GPIOB
#define BSP_USART_RX_RCU RCU_GPIOA
2
3
然后对应的使能时钟的代码就是
rcu_periph_clock_enable(BSP_USART_RCU); // 开启串口时钟
rcu_periph_clock_enable(BSP_USART_TX_RCU); // 开启端口时钟
rcu_periph_clock_enable(BSP_USART_RX_RCU); // 开启端口时钟
2
3
配置GPIO复用模式
GD32的引脚是可以有复用功能的,就是说单个引脚可有很多个功能,默认的功能一般都是作为GPIO使用。在gd32vw55x_gpio.h中可以查找到设置复用的函数
void gpio_af_set(uint32_t gpio_periph, uint32_t alt_func_num, uint32_t pin);
这个函数有三个参数,第一个参数gpio_periph就是要配置的引脚端口,第二个参数alt_func_num就是要复用的功能,第三个参数pin就是要配置的引脚。第一个参数和第三个参数我们知道分别为GPIOA,GPIO_PIN_8,GPIOB,GPIO_PIN_15。关于第二个参数可以到数据手册的第24页进行查找,如图所示。
从上图可以看到PA8对应的USART0_RX的功能复用为AF2,PB15对应的USART0_TX的功能复用为AF8,那第二个参数我们也知道了。接下来就可以开启PA8,PB15的复用功能了。代码编写如下。首先定义一下端口和复用功能的宏定义。
#define BSP_USART_TX_PORT GPIOB
#define BSP_USART_TX_PIN GPIO_PIN_15
#define BSP_USART_TX_AF GPIO_AF_8
#define BSP_USART_RX_PORT GPIOA
#define BSP_USART_RX_PIN GPIO_PIN_8
#define BSP_USART_RX_AF GPIO_AF_2
2
3
4
5
6
7
然后调用复用函数使能复用功能。
/* 配置复用功能 */
gpio_af_set(BSP_USART_TX_PORT,BSP_USART_TX_AF,BSP_USART_TX_PIN);
gpio_af_set(BSP_USART_RX_PORT,BSP_USART_RX_AF,BSP_USART_RX_PIN);
2
3
通过上面两句就可以把PA8,PB15设置为串口功能了。
配置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);
2
3
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
3
4
配置串口
和之前库函数点灯不同的是,使用串口还需要进行串口的配置。前面只是对GPIO的引脚做了配置,然后就可以操作GPIO进行输入输出,但是要使用串口,还需要设置串口的一些参数,后面使用其它的资源也是如此。
在前一章节的串口原理介绍中讲到串口有几个必要的参数,分别是波特率,校验位,数据位,停止位。在代码里也就是要设置这几个参数。关于串口函数的查找,推荐大家到固件库的gd32vw55x_uart.h
中去查找对应的函数,两者搭配起来使用效果最佳。
在使用一个外设之前,可以先复位一下,防止一些不必要的事情发生。在对应的外设库文件中一般都会有这个函数。在gd32vw55x_uart.h
头文件中有
void usart_deinit(uint32_t usart_periph);
这个函数会复位串口。有一个参数usart_periph
,就是要复位的串口端口。
void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);
上面这个函数设置波特率。有两个参数,分别为要设置的串口usart_periph
和要设置的波特率baudval
。
void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);
这个函数配置校验功能。有两个参数,第一个usart_periph
就是要配置的串口,第二个paritycfg
就是设置校验方式。关于校验方式的选项如图所示。
从图可以看到,校验方式可以选择奇校验、偶检验和无校验。一般配置为无校验即可。
void usart_word_length_set(uint32_t usart_periph, uint32_t wlen);
这个函数设置数据位的长度。有两个参数,第一个参数usart_periph
就是要配置的串口,第二个参数wlen
就是要设置的数据位的长度,关于数据位长度的选项如图所示。
从图可以知道关于数据长度有两个选项,一般选择8位即可。
void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen);
这个函数设置停止位的位数。有两个参数,第一个参数usart_periph
就是要配置的串口,第二个参数stblen
就是要设置的停止位的位数,关于停止位的位数的选项如图所示。
停止位一般选择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位停止位
2
3
4
5
6
这里要注意一下BSP_USART是一个宏定义,其实就是要使能的串口。
#define BSP_USART USART0
上面示例中 dwbaud_rate
是函数里的一个形参,为了方便修改串口的波特率,函数实体为
void uart0InitConfig(uint32_t dwbaud_rate);
在使用的时候,可以直接传入对应的波特率,例如配置波特率为115200,转化为代码可写为
void uart0InitConfig(115200); // 串口配置
如还需要配置其它的参数请查找对应的函数调用即可。
使能串口
串口配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。值得注意的是发送和接收也需要分别去使能,串口使能相当于一个总开关,发送和接收相当于分别控制的开关。要使用串口,首先要打开总开关
void usart_enable(uint32_t usart_periph);
这个函数使能串口,有一个参数,就是要使能的串口。
然后如果要发送数据的话还需要使能发送功能。
void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);
这个函数配置串口发送。有两个参数,第二个参数txconfig
对应的选项如图所示。
从图中可以看到,如果使能串口发送就配置为USART_TRANSMIT_ENABLE
。
如果要接收数据的话还需要使能接收功能。
void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);
这个函数配置串口接收。有两个参数,其中第二个参数对应的选项如图所示。
从图中可以看到,如果使能串口接收就配置为USART_RECEIVE_ENABLE
。
使能串口配置如下:
//使能串口接收
usart_receive_config(BSP_USART, USART_RECEIVE_ENABLE);
//使能串口发送
usart_transmit_config(BSP_USART, USART_TRANSMIT_ENABLE);
//使能串口
usart_enable(BSP_USART);
2
3
4
5
6
串口发送数据
配置好串口之后,下一步的操作就是要发送数据。
void usart_data_transmit(uint32_t usart_periph, uint16_t data);
这个函数可以发送数据,有两个参数,第一个参数usart_periph
是要使用的串口,第二个参数data
是要发送的数据,不过需要注意的是这个函数一次只能发送一个字节。要保证串口稳定的传输,就需要在发送完一个字节之后再发送下一个字节。需要去检测数据发送完成。
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag);
这个函数是获取状态寄存器的标志。有两个参数,第一个参数usart_periph
是要使用的串口,第二个参数flag
是要获取的状态位,状态位选项为图所示(只截取一部分)。
可以获取发送缓冲区的标志位,看当前是否还有数据,如果有数据,将等待,如果没有数据就可以继续发送。
关于TBE的介绍如图所示。
从图可以了解到当将要发送的数据写入USART_DATA
时,此位被清0,当数据发送完成之后,此位置1。所以当检测到此位为1时就表明当前数据缓冲区为空,可以继续发送数据。
关于串口发送数据可以封装为一个函数如下。
void UsartSendByte(uint8_t ucch)
{
usart_data_transmit(BSP_USART, (uint8_t)ucch);
while(RESET == usart_flag_get(BSP_USART, USART_FLAG_TBE)); // 等待发送数据缓冲区标志置位
}
2
3
4
5
当我们调用
UsartSendByte('h');
UsartSendByte('e');
UsartSendByte('l');
UsartSendByte('l');
UsartSendByte('o');
2
3
4
5
将会在串口助手打印hello,如图所示。
这样一个字符一个字符的打印输出是不是很麻烦,没关系,我们可以再进行封装一层,一次发送一个字符串。
void UsartSendString(uint8_t *ucstr)
{
while(ucstr && *ucstr) // 地址为空或者值为空跳出
{
UsartSendByte(*ucstr++);
}
}
2
3
4
5
6
7
通过调用
UsartSendString("hello\r\n");
这一句就可以打印出hello。
串口重定向介绍
上面封装的发送字符串的方式打印信息看似方便,但如果我们想要打印数字,小数,该怎么打印呢?大家是否习惯使用了printf
这个函数,可以通过%d,%f
打印整形和小数,这一小节就教大家怎么把串口重定向到printf
函数。
C语言中的printf函数默认输出设备是显示器,如果要在串口显示,必须重新定义标准库函数里调用的与输出设备相关的函数。需要注意的是,在NucleiStudio
中使用printf
需要全版本。由于支持全格式的时候占用的代码量和堆栈空间比较大,一般编译器默认都是实现标准甚至是最小的版本。
在工程设置中将下图中的打勾去除即可;
printf重定向
在NucleiStudio+GCC环境下,c语言的printf函数中会不断循环调用_write
函数。而关于printf的使用,GD32官方已经为我们移植好了 在工程路径Firmware/RISCV/stubs/write.c
中,_put_char
函数里,默认使用的是USART0
输出到printf
中。如想修改为串口1发送,则将USART0
修改为UART1
即可。
确定之后就可以使用printf函数输出信息了。
串口接收数据
串口接收可以使用轮询接收,中断接收,DMA接收。通常是使用中断方式进行接收,当接收到串口数据时,马上进入中断处理。
接收中断配置
接收数据是放在中断中进行接收的,所以要使能串口的接收中断,既然要操作中断那肯定要配置中断的优先级,还是按照原来的中断分组,2个抢占优先级2个响应优先级。这里配置串口的优先级可以配置为抢占优先级为2,响应优先级为2。配置好中断后,要使能什么情况下进入中断,比如是刚接收数据就进入中断还是接收完成之后再进入中断。这里关于串口中断选项如图所示:
USART_INT_PERR
:优先级错误中断USART_INT_TBE
:发送缓冲区空中断。USART_INT_TC
:发送完成中断。USART_INT_RBNE
:接收缓冲区不为空中断和溢出错误中断。如果开启了这个中断,每当接收到一个字符,就会触发这个中断。USART_INT_IDLE
:空闲检测中断。如果开启了这个中断,将会在一帧数据传输完成之后触发中断,一般用来判断一帧数据是否传输完成。USART_INT_LBD
:LIN断开信号检测中断USART_INT_CTS
:CTS中断USART_INT_ERR
:错误中断USART_INT_EB
:块结束事件中断USART_INT_RT
:接收超时事件中断
从上面可以看到串口中断的标志位有很多,但是串口中断接收需要配置的中断一般有两个,分别是USART_INT_RBNE
和USART_INT_IDLE
,用USART_INT_RBNE
中断来接收每一个字符的数据,用USART_INT_IDLE
中断来判断一帧数据是否传输完成。
串口中断的宏定义如下:
#define BSP_USART_IRQ USART0_IRQn
串口中断配置如下:
/* 使能读数据缓冲区非空中断和过载错误中断 */
usart_interrupt_enable(BSP_USART, USART_INT_RBNE);
/* 使能IDLE线检测中断 */
//usart_interrupt_enable(BSP_USART, USART_INT_IDLE);
/* 配置中断优先级 */
eclic_irq_enable(BSP_USART_IRQ, 2, 2);
2
3
4
5
6
这里需要注意,不建议在串口初始化时开启IDLE空闲中断,否则将会在一上电的时候就会自动进入一次空闲中断。关于空闲中断的开启,在后面中断服务函数章节进行说明。
串口变量定义
单片机要实现接收数据,也就是说要通过上位机给单片机发送数据,那单片机需要接收数据然后存储起来,所以需要定义一个数组g_recv_buff
去存储接收到的数据,那这个数组定义多大呢,这要根据数据量去判断,为了支持更好的修改,定义一个宏USART_RECEIVE_LENGTH
来表示数组的大小。为了方便处理数据,可以定义一个变量g_recv_length
去记录串口实际接收到的数据的个数。
怎么去判断数据传输完成呢?也就是说什么时候开始处理数据呢?前面说了USART_INT_IDLE
可以用来判断一帧数据是否传输完成,那可以定义一个变量g_recv_complete_flag
在USART_INT_IDLE
中调用,当g_recv_complete_flag
置 1 的时候说明数据传输完成了,然后就可以去处理数据了。
宏定义如下:
/* 串口缓冲区的数据长度 */
#define USART_RECEIVE_LENGTH 4096
2
变量定义如下:
uint8_t g_recv_buff[USART_RECEIVE_LENGTH]; // 接收缓冲区
uint16_t g_recv_length = 0; // 接收数据长度
uint8_t g_recv_complete_flag = 0; // 接收完成标志位
2
3
串口接收中断服务函数
串口接收数据要到中断中去处理,前面串口中断配置的时候,我们打开了USART_INT_RBNE
这个中断,那在中断服务函数里面也需要对这个中断进行判断。
FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
这个函数获取串口中断的标志位状态。有两个参数,第一个参数usart_periph
就是要获取的串口外设,第二个参数int_flag
就是要获取的串口中断的标志位。关于串口中断的标志如图所示。
从图可以看到,串口中断标志和之前的串口中断非常相似,根据命令可以很方便的找到对应的关系,USART_INT_RBNE
对应于USART_INT_FLAG_RBNE
。在检测到USART_INT_FLAG_RBNE
中断标志被置1之后,就说明当前缓冲区不为空了,有数据到来了,我们要做的操作就是把当前数据读出来然后保存到到接收数组中,然后对数据长度进行加一,等待下一次数据存储。
uint16_t usart_data_receive(uint32_t usart_periph);
这个函数可以接收数据。有一个参数usart_periph
就是要读取的串口外设。
在接收数据的过程中,可以判断是否开启了空闲中断,如果没有开启则开启空闲中断线。那么如何判断是否开启了空闲中断?从用户手册中我们可以知道,空闲中断的使能是在 USART_CTL0 中的第四位进行控制。
通过寄存器中各个位下的指示 rw
,可以知道该寄存器属于可读可写寄存器,r
代表可以读,w
代表可以写。那么我们可以这么写判断:
//判断空闲中断是否开启,如果没有开启 ( USART_CTL0 的 第4位是否为0,为0说明当前没有开启空闲中断 )
if( (USART_CTL0(BSP_USART) & (1<<4) ) == 0 )
{
/* 使能IDLE线检测中断 */
usart_interrupt_enable(BSP_USART, USART_INT_IDLE);
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
}
2
3
4
5
6
7
8
由此我们开启的空闲中断,当串口数据接收完成之后,就会进入到空闲中断中,然后将g_recv_complete_flag
变量置1,说明可以进行数据处理。需要注意的是,在每一个中断满足的时候,都需要执行去清除中断标志的操作,不然数据会出现混乱。
// 检测到空闲中断
if(usart_interrupt_flag_get(BSP_USART,USART_INT_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
/* 数据结尾加上字符串结束符 */
g_recv_buff[g_recv_length] = '\0';
/* 接收完成标志位 */
g_recv_complete_flag = 1;
/* 关闭空闲中断 */
usart_interrupt_disable(BSP_USART, USART_INT_IDLE);
}
2
3
4
5
6
7
8
9
10
11
12
串口中断服务函数的宏定义如下:
#define BSP_USART_IRQHandler USART0_IRQHandler
串口中断服务函数如下:
void BSP_USART_IRQHandler(void)
{
// 接收缓冲区不为空
if(usart_interrupt_flag_get(BSP_USART,USART_INT_FLAG_RBNE) != RESET)
{
/* 清除接收中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_RBNE);
/* 把接收到的数据放到缓冲区中 */
g_recv_buff[g_recv_length] = usart_data_receive(BSP_USART);
/* 限制接收长度防止数据溢出 */
g_recv_length=( g_recv_length + 1 ) % USART_RECEIVE_LENGTH;
//判断空闲中断是否开启,如果没有开启 ( USART_CTL0 的 第4位是否为0,为0说明当前没有开启空闲中断 )
if( (USART_CTL0(BSP_USART) & (1<<4) ) == 0 )
{
/* 使能IDLE线检测中断 */
usart_interrupt_enable(BSP_USART, USART_INT_IDLE);
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
}
}
// 检测到空闲中断
if(usart_interrupt_flag_get(BSP_USART,USART_INT_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
/* 数据结尾加上字符串结束符 */
g_recv_buff[g_recv_length] = '\0';
/* 接收完成标志位 */
g_recv_complete_flag = 1;
/* 关闭空闲中断 */
usart_interrupt_disable(BSP_USART, USART_INT_IDLE);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
串口中断接收数据处理
为了更方便去观察数据,可以将每次接收的数据打印到串口上显示,看看是不是和我们发送的数据一致。在main函数中while下添加以下代码:
/* 等待数据传输完成 INTERRUPT */
if(g_recv_complete_flag) // 数据接收完成
{
g_recv_complete_flag = RESET; // 等待下次接收
printf("g_recv_length:%d ",g_recv_length);
printf("Interrupt recv:%s\r\n",g_recv_buff); // 打印接收的数据
memset(g_recv_buff,0,g_recv_length); // 清空数组
g_recv_length = 0;
}
2
3
4
5
6
7
8
9
每次处理完数据后都要记得把对应的标志位和数组都清零。
到此,编译下载到开发板就可以使用串口中断接收功能了。
实验现象
相关代码
串口中的.c文件全部代码
#include "bsp_usart.h"
#include "stdio.h"
uint8_t g_recv_buff[USART_RECEIVE_LENGTH]={0}; // 接收缓冲区
uint16_t g_recv_length = 0; // 接收数据长度
uint8_t g_recv_complete_flag = 0; // 接收完成标志位
/**
* 串口0初始化配置
* @param dwbaud_rate 波特率设置
*/
void Uart0InitConfig(uint32_t dwbaud_rate)
{
rcu_periph_clock_enable(BSP_USART_RCU); // 开启串口时钟
rcu_periph_clock_enable(BSP_USART_TX_RCU); // 开启端口时钟
rcu_periph_clock_enable(BSP_USART_RX_RCU); // 开启端口时钟
/* 配置复用功能 */
gpio_af_set(BSP_USART_TX_PORT,BSP_USART_TX_AF,BSP_USART_TX_PIN);
gpio_af_set(BSP_USART_RX_PORT,BSP_USART_RX_AF,BSP_USART_RX_PIN);
/* 配置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);
/* 配置TX为推挽输出 50MHZ */
gpio_output_options_set(BSP_USART_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, BSP_USART_TX_PIN);
/* 配置RX为推挽输出 50MHZ */
gpio_output_options_set(BSP_USART_RX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, BSP_USART_RX_PIN);
/* 串口参数配置 */
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位停止位
/* 使能串口接收 */
usart_receive_config(BSP_USART, USART_RECEIVE_ENABLE);
/* 使能串口发送 */
usart_transmit_config(BSP_USART, USART_TRANSMIT_ENABLE);
/* 使能读数据缓冲区非空中断和过载错误中断 */
usart_interrupt_enable(BSP_USART, USART_INT_RBNE);
/* 配置中断优先级 */
eclic_irq_enable(BSP_USART_IRQ, 2, 2);
/* 使能串口 */
usart_enable(BSP_USART);
}
/**
* 发送单个字符
* @param ucch 发送的单个字节字符
*/
void UsartSendByte(uint8_t ucch)
{
usart_data_transmit(BSP_USART, (uint8_t)ucch);
while(RESET == usart_flag_get(BSP_USART, USART_FLAG_TBE)); // 等待发送数据缓冲区标志置位
}
/**
* 发送字符串
* @param ucstr 发送的字符串
*/
void UsartSendString(uint8_t *ucstr)
{
while(ucstr && *ucstr) // 地址为空或者值为空跳出
{
UsartSendByte(*ucstr++);
}
}
/*!
\brief 这个函数处理USART RBNE中断请求和空闲中断请求
\param[in] none
\param[out] none
\retval none
*/
void BSP_USART_IRQHandler(void)
{
// 接收缓冲区不为空
if(usart_interrupt_flag_get(BSP_USART,USART_INT_FLAG_RBNE) != RESET)
{
/* 清除接收中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_RBNE);
/* 把接收到的数据放到缓冲区中 */
g_recv_buff[g_recv_length] = usart_data_receive(BSP_USART);
/* 限制接收长度防止数据溢出 */
g_recv_length=( g_recv_length + 1 ) % USART_RECEIVE_LENGTH;
//判断空闲中断是否开启,如果没有开启 ( USART_CTL0 的 第4位是否为0,为0说明当前没有开启空闲中断 )
if( (USART_CTL0(BSP_USART) & (1<<4) ) == 0 )
{
/* 使能IDLE线检测中断 */
usart_interrupt_enable(BSP_USART, USART_INT_IDLE);
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
}
}
// 检测到空闲中断
if(usart_interrupt_flag_get(BSP_USART,USART_INT_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志位 */
usart_interrupt_flag_clear(BSP_USART, USART_INT_FLAG_IDLE);
/* 数据结尾加上字符串结束符 */
g_recv_buff[g_recv_length] = '\0';
/* 接收完成标志位 */
g_recv_complete_flag = 1;
/* 关闭空闲中断 */
usart_interrupt_disable(BSP_USART, USART_INT_IDLE);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
串口中的.h文件全部代码
#ifndef BSP_USART_H
#define BSP_USART_H
#include "gd32vw55x.h"
//时钟定义
#define BSP_USART_RCU RCU_USART0
#define BSP_USART_TX_RCU RCU_GPIOB
#define BSP_USART_RX_RCU RCU_GPIOA
//串口发送引脚定义
#define BSP_USART_TX_PORT GPIOB
#define BSP_USART_TX_PIN GPIO_PIN_15
#define BSP_USART_TX_AF GPIO_AF_8
//串口接收引脚定义
#define BSP_USART_RX_PORT GPIOA
#define BSP_USART_RX_PIN GPIO_PIN_8
#define BSP_USART_RX_AF GPIO_AF_2
//串口定义
#define BSP_USART USART0
#define BSP_USART_IRQ USART0_IRQn
#define BSP_USART_IRQHandler USART0_IRQHandler
/* 串口缓冲区的数据长度 */
#define USART_RECEIVE_LENGTH 255
extern uint8_t g_recv_buff[USART_RECEIVE_LENGTH]; // 接收缓冲区
extern uint16_t g_recv_length; // 接收数据长度
extern uint8_t g_recv_complete_flag; // 接收完成标志位
/* 串口0初始化配置 */
void Uart0InitConfig(uint32_t dwbaud_rate);
/* 串口0发送单个字符 */
void UsartSendByte(uint8_t ucch);
/* 串口0发送字符串 */
void UsartSendString(uint8_t *ucstr);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
在main.c中编写如下代码:
#include "gd32vw55x.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "bsp_usart.h"
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/* 使能全局中断 */
eclic_global_interrupt_enable();
/* 中断分组 */
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL2_PRIO2);
// 滴答当定时器初始化
systick_config();
// 串口0初始化波特率位115200
Uart0InitConfig(115200U);
UsartSendString("UsartSendString test\r\n");
printf("printf test. float:%f int:%d\r\n", 12.512, 10);
while(1)
{
/* 等待数据传输完成 */
if(g_recv_complete_flag==1) // 数据接收完成
{
g_recv_complete_flag = 0; // 等待下次接收
printf("g_recv_length:%d ",g_recv_length);
printf("Interrupt recv:%s\r\n",g_recv_buff); // 打印接收的数据
memset(g_recv_buff,0,g_recv_length); // 清空数组
g_recv_length = 0;//清空长度
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
实验现象
上电开发板通过串口发送字符串内容
UsartSendString test
printf test. float:12.512000 int:10
2
关于这一章节的代码,可以在开发板资料/03 - 软件资料/代码例程/里面的008串口信息收发
。
下载中心跳转📦
资料下载中心:点击跳转🚀
然后等待接收数据。当接收到数据时,回显出接收到的内容;