GD32E103C8T6《调试篇》之USART + 超时检测 (附代码)

前言

PCB板是现成的,之前用的USART0,发现不对;在老工程师的指导下,发现必须要根据板的IO配置来编程。
芯片:GD32E103C8T6
编译环境:keil V5.35.0.0 / MDK-ARM V5.35.0.2

串口知识点

USART——通用同步异步收发器,串行通信设备,全双工数据交换

USART 和 UART 区别本篇不做探讨

串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据;
串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息
在这里插入图片描述

1、参看GD32E103的Datasheet,找到对应芯片的封装(重要)

因为我就找错过一次,明明芯片是48脚,却看的100脚的

在这里插入图片描述

2、查看管脚详细信息,找到对应芯片的封装(重要)

在这里插入图片描述
在这里插入图片描述
可以看见GD32103C8的USART有3个可以用(0-2)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

PCB已经决定了GD32E103的21脚连接485芯片的4脚;22脚连接485芯片的1脚,参看103的数据手册
21脚是PB10, 22脚是PB11 ;
GPIO对应485芯片:PB10–Tx ,PB11–Rx;
这样一来用串口几通讯也限制了,看图,我只能用USART2
在这里插入图片描述

3、利用RS485芯片(75176B)和外部通讯

由于我的目标板通过一个485芯片和外部通讯,所以会用到一根USB TO RS485的工具
在这里插入图片描述

1)485芯片封装

在这里插入图片描述

2)RS485芯片使用说明

RS485——单工、半双工
一般普通的485电路,用单片机的RXD连接485芯片的RO引脚、用单片机的TXD连接485芯片的DI引脚,485芯片的2、3脚接在一起,连接至单片机某一个引脚(本文例子连的单片机19脚——PB1);
在这里插入图片描述

当单片机要发送数据的时候,把PB1置1,数据通过TXD发送出去。

当单片机要接收数据的时候,把PB1置0,数据通过RXD接收回来。

总结:RE/DE为低电平时发送禁止,接收有效,RE/DE为高电平时,则发送有效,接收截止;
在这里插入图片描述

4、单片机发送数据,通过串口打印

单片机发送数据,所以485芯片的配置应该是把PB1置1

 gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); 
 gpio_bit_set(GPIOB,GPIO_PIN_1);	//TX -- 1

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>

#include "gd32e10x.h"
#include <stdio.h>
//PB10--Tx  PB11--Rx

void usart2_init() // 初始化串口2
{
	rcu_periph_clock_enable(RCU_GPIOB);  // 使能GPIOA时钟
	rcu_periph_clock_enable(RCU_USART2);  // 使能串口2时钟
	
    /* connect port to USARTx_Tx *///配置TX 推挽复用模式
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);

    /* connect port to USARTx_Rx *///配置RX 浮空输入模式 
    gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_11); 
	
	/*****  485 TX enable  ****/ //pin19--PB1
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); //GPIO_OSPEED_2MHZ
    gpio_bit_set(GPIOB,GPIO_PIN_1);	//TX -- 1
	
	// 步骤1-7:
	usart_deinit(USART2); 
	usart_word_length_set(USART2,USART_WL_8BIT);  ///2、配置USART字长
	usart_stop_bit_set(USART2,USART_STB_1BIT);  ///3、配置USART停止位
	usart_baudrate_set(USART2,115200);  ///5、配置USART波特率
	usart_transmit_config(USART2,USART_TRANSMIT_ENABLE);  // 6、USART发送配置
	usart_receive_config(USART2,USART_RECEIVE_ENABLE);
		
	usart_enable(USART2);//使能串口		
}
int main(void)
{	
	systick_config(); //初始化延时函数
	delay_ms(100);
	usart2_init();	
	while(1)
	{
		printf("****yang****");
		printf("\r\n");
		delay_ms(1000);
		printf("****fa****");
		printf("\r\n");
		delay_ms(1000);
	}
}

// 中断处理函数
void USART2_IRQHandler(void)
{	
	// 串口2外部给串口2发送了数据,就会进入下面这个中断,然后把数据读取到data里面
	if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) )// 发生中断,则返回RESET
	{
		 usart_data_receive(USART2); // 读取串口2接收到的数据		
	}
	 usart_interrupt_flag_clear(USART2, USART_INT_FLAG_EB);
}
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(USART2, (uint8_t)ch);
    while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));
    return ch;
}

效果
在这里插入图片描述

5、串口外部向单片机发送数据,单片机接收数据后,回显数据(有超时检测)

1)查看固件库使用手册

找到设置USART接收超时阈值的函数
在这里插入图片描述
使能接收超时函数
在这里插入图片描述
配置时间,使能超时

	usart_receiver_timeout_threshold_config(USART2,500);
	usart_receiver_timeout_enable(USART2);

使能相应的子中断
在这里插入图片描述

	usart_interrupt_enable(USART2,USART_INT_RBNE);
	usart_interrupt_enable(USART2,USART_INT_RT);

———————————————————————————————————
中断处理函数里
由于使能相应的子中断,所以选择对应的中断标志
在这里插入图片描述
在这里插入图片描述

if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) )
{
//不需要手动清除
}
if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RT) )
{//需要手动清除
	usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RT);//超时事件中断标志
}
usart_interrupt_flag_clear(USART2, USART_INT_FLAG_EB);//块结束事件中断标志 USART2总中断清除

Q:为什么USART_INT_FLAG_RBNE不需要手动清除,USART_INT_FLAG_RT需要手动清除
A:查看用户数据手册,找到17章(USART),再找17.4(USART状态寄存器)
在这里插入图片描述
在这里插入图片描述
重点来了!!!敲黑板!!!
————————————————————
软件可以通过对该位写0 读USART_DATA寄存器来将该位清0
意思就是可以手动清除,或者寄存器自动清0
————————————————————
软件可以通过对该位写0,没有或者
意思就是只能手动清除
可以试试把usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RT);注释了,你会发现点击发送数据就不会回送给串口了;
这里菜鸟我请教的老工程师,自己是琢磨不出来的;

2)代码

#include "common.h"
//PB10--Tx  PB11--Rx

#define BUFSIZE 64  
typedef struct
{
	u8 recved;
	u8 len;
	u8 rxbuf[BUFSIZE];
	u8 rxidx;
}ts_uart;


ts_uart logo;

void usart2_init() // 初始化串口2
{
	rcu_periph_clock_enable(RCU_GPIOB);  // 使能GPIOB时钟
	rcu_periph_clock_enable(RCU_USART2);  // 使能串口2时钟
	
    /* connect port to USARTx_Tx *///配置TX 推挽复用模式
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);

    /* connect port to USARTx_Rx *///配置RX 浮空输入模式 
    gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_11); //
	
	/*****  485 TX enable  ****/ //pin19--PB1	
	gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1); //GPIO_OSPEED_2MHZ
	gpio_bit_reset(GPIOB,GPIO_PIN_1); //0-Rx  
	
	// 步骤1-7:
	usart_deinit(USART2); //1、
	usart_word_length_set(USART2,USART_WL_8BIT);  ///2、配置USART字长
	usart_stop_bit_set(USART2,USART_STB_1BIT);  ///3、配置USART停止位
	usart_baudrate_set(USART2,115200);  ///5、配置USART波特率
	usart_transmit_config(USART2,USART_TRANSMIT_ENABLE);  // 6、USART发送配置
	usart_receive_config(USART2,USART_RECEIVE_ENABLE);
	
	usart_receiver_timeout_threshold_config(USART2,500);//超时检测  500———4340us  RT该位域用于指定接收超时值,单位是波特时钟的时长。 标准模式下,如果在最后一个字节接收后,在RT规定的时长内,没有检测到新的起始位,USART_STAT1寄存器中RTF标志被置位。 
	usart_receiver_timeout_enable(USART2);
	
		
	usart_enable(USART2);//使能串口	
	
//	// 在nvic中配置中断向量和中断优先级
	nvic_irq_enable(USART2_IRQn,0,0); //使能NVIC的中断
//	// 使能USART子中断   
	usart_interrupt_enable(USART2,USART_INT_RBNE);
	usart_interrupt_enable(USART2,USART_INT_RT);
}


int main(void)
{	
	u8 i;
	systick_config(); //初始化延时函数
	delay_ms(100);
	usart2_init();
	
   while(1)
    {		
		if(logo.recved)
		{
			gpio_bit_set(GPIOB,GPIO_PIN_1); //0-Rx  1-Tx
			delay_ms(1);//延时是有必要的 485的RX、TX切换需要时间
			
			for(i=0;i<logo.len;i++)
			{			
				usart_data_transmit(USART2, logo.rxbuf[i]);
				while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));//手动清除
			}
			
			logo.rxidx=0;
			logo.recved=0;
			
			gpio_bit_reset(GPIOB,GPIO_PIN_1); //0-Rx 不发数据时,默认Rx open; 
		}		
	}
}

// 中断处理函数
void USART2_IRQHandler(void)
{
	unsigned char data;
	
	// 串口2外部给串口2发送了数据,就会进入下面这个中断,然后把数据读取到data里面
	//usart_interrupt_flag_get获取USART中断标志位状态;USART_INT_FLAG_RBNE读数据缓冲区非空中断标志 
	
	//get a byte
	if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RBNE) )// 发生中断,则返回RESET
	{
		data = usart_data_receive(USART2); // 读取串口2接收到的数据
		if(logo.rxidx < BUFSIZE)
		{
			logo.rxbuf[logo.rxidx++] = data;			
		}		
	}
	
	//rx timeout check
	if( RESET != usart_interrupt_flag_get(USART2,USART_INT_FLAG_RT) )// 接收超时标志
	{
		logo.len = logo.rxidx;
		logo.recved = 1;  //接收完成标志 
		usart_interrupt_flag_clear(USART2, USART_INT_FLAG_RT);//清除接收超时中断
	}	

	usart_interrupt_flag_clear(USART2, USART_INT_FLAG_EB);//USART2总中断清除

}

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
    usart_data_transmit(USART2, (uint8_t)ch);
    while(RESET == usart_flag_get(USART2, USART_FLAG_TBE));
    return ch;
}

效果:

jsrecord_2024-01-04-17-26-43

补充:

115200bps bit/s 1bit ------ 1000000 us
1+8+1(起始位+数据位+停止位) 10bit ------ 约86us
在这里插入图片描述

一帧数据传送需要86us , 超时检测的时间设置太长,会延迟回显数据;我做了实验,超时检测时间设为10也就是86us,很快能收到,设成1都行;设为115200会延时1s;
一帧数据要保证传输完成,再发送,所以接收完成标志是有必要的;

本文如有错误之处,请予以指正!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值