USUART代码例程和库函数



提示:以下是本篇文章正文内容,下面案例可供参考

一、USUART中重要的寄存器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此上个寄存器的作用:
TXE表示发送缓存区【TDR】是否为空的标志。如果TDR里有暂放数据,即其没空,此时TXE=0。当把TDR里的数据COPY到移位寄存器里了且没放新数据进TDR时,TXE=1.

TC 表示从TDR里过来的数据是否全部移到外面的TX线上去了的标志。如果从TDR过来的数据全部被移送到TX线而且此时TDR里也没有新的数据,则TC=1。相反,如果刚才从TDR里过来的数据还没有全部移到外面去,或者说虽然之前TDR里的数据被移走了,但TDR里又来了新的数据,此时TC=0。平常大家把TC称之为传输结束标志,没说错,但有时可能会带来些误解,误解就出在“结束”这个字眼上。

总结:TBE,当TBE标志位置1时,可以把新数据写入到数据寄存器。
TC,当TC标志位为1时,表示一串字节发送完毕。
RBNE:当其被置1时,可以从数据寄存器中读取下一个字节。
USART_INT_RBNE:接收缓冲区不为空中断和溢出错误中断。如果开启了这个中断,每当接收到
一个字符,就会触发这个中断。在检测到 USART_INT_FLAG_RBNE 中断标志被置 1 之后,就说明当前缓冲区不为空了,有数据到来了,我们要做的操作就是把当前数据读出来然后保存

USART_INT_IDLE:空闲检测中断。如果开启了这个中断,将会在一帧数据传输完成之后触发中
断,一般用来判断一帧数据是否传输完成。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

二、USART中发送数据。

发送数据的理解:数据发送时一个字节发送一次的。
注意:
a中字节移动到b中,TBE 会被置1。
在这里插入图片描述
在hal库中,可以看到usart_data_transmit()在低层上是给数据寄存器写入数据data。也就是说在代码中对应的发送功能,并不是指发送数据到TX引脚接收的全过程。**代码中对应的发送功能仅仅是把数据写入数据寄存器(TDR)。**剩下的过程由stem32自动完成(对应过程见图中:a到b到TX引脚)
在这里插入图片描述

对应在代码的表现为:
在这里插入图片描述
当一调用usart data transmit(),会立刻向发生数据寄存器写入一个字节。
而后的数据到移位寄存器,再到TX引脚的过程,由硬件自动完成。
在这里插入图片描述

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

由于未等待数据寄存器(即缓存寄存器)为空就直接调用usart_data_transmit(),会立即给该寄存器写了新的字节,这会导致原来的字节未移动到位移寄存器就被覆盖了。写入数据到数据寄存器(也就是usart_data_transmit())会自动清空标志位TBE,用户手册有相关介绍,这样写的时候TBE置0,等待TBE置1,然后写入下一个字节。

三、接收数据

在这里插入图片描述

由于数据接收时,不知道外部设备何时会来数据,这个不像发数据,发送数据完全由自己控制。
目前有二种方法,接收数据。
方法一:查询模式,查询的流程是:在主函数里面不断判断RXNT的标志位,如果置1了,就调用receivedata,读取数据寄存器。读取数据寄存器(也就是receive_data)会自动清空标志位RXNT,用户手册有相关介绍。
方法二:中断方法,其流程是:首先初始化加入开启中断的代码(开启RXNT标志位中断),当RXNT的标志位被置1,就会调用中断函数,可以自定义中断函数中写入usart_receive_data( )相关代码,来读取数据寄存器。

这就意味着,开启接收中断后,发送端来一个字节,会立刻把该字节送入到位移寄存器中,当位移寄存器中数据移动到数据寄存器中时,RXNT的标志位被置1,中断立刻开始执行,此时我们
usart_receive_data( )来获取数据寄存器的数据即可。

但是如果在中断函数内部,获取数据usart_receive_data( )后面,接一个延迟函数,而发送端来了一个新数据时,而且数据已经到了数据寄存器中,此时会再次触发一个的中断,但由于二个中断优先级一样,会先处理旧数据触发的中断,获取数据,但此时寄存器数据已经被新数据替换,所以获取到的数据是新数据,而后此中断结束,进入有新数据触发的中断,再一次获取新数据,这样就获取了二次新数据。

这里科普一下,从发送端发出数据,到进入stem32的RX端口,再到移位寄存器的过程,是stem32自发完成的。
我们需要做的的,只是根据RXNT标志位,判断数据是进入到数据寄存器,如果数据寄存器有数据,就马上读取出数据寄存器中的内容,在代码中对于操作是usart_receive_data( )。

接收一包字节的时候,可以在读取这一包字节最后一个字节时 置 一个读取结束的标志位,同时在主函数里面一直查询该标志位,当该标志为1是,就对这一包数据进行接收或者处理。而且注意在数据采集处理后,一定要对标志位清零,方便下一个数据包字节的接收,使其标志位重新起作用。

如果接受速度小于发送端发送数据的速度呢?

一包字节采用的是一个数组来存放该一包字节,然后根据自定义的标志位来时刻判断该数据是否存满一包,如果发送端只发一包数据,也不会出现数据来不及接收的现象,

但是当发送端多包字节连续发送,发送数据的速度,远大于stem32接受的速度,就会出现用于存放一包数据的数组,被下一包数据替换。
此时可以把存放数据的数组理解为数据寄存器(容器),

过程是这样的:当发送端来了数据,数据会自动进入数据寄存器,触发中断,然后自动保存在数组中,此时如果不保存接受处理该一包数据,后面如果马上来了新的数据,也会自动把原来在数组的数据替换。

就是说容器空间有限,发送来的数据会自动保存(32自动完成的)到容器中,在下一个数据来之前,需要把容器中数据保存处理,否则会被新来的数据替换掉。

四、USART发送数据示例代码(print重定向)

在这里插入图片描述

main文件
int main(void)
{	
	SystickInit();
	LedDrvInit();
	KeyDrvInit();
	Usb2ComDrvInit();
	Usb2ComTest();
	while (1)
	{

	}
}
usb2com_drive.c文件
#include <stdint.h>
#include <stdio.h>
#include "gd32f30x.h"

static void Usb2ComGpioInit(void)
{
	rcu_periph_clock_enable(RCU_GPIOA);
	//配置TX引脚对于的管脚为复用推挽输出模式,输出模式有复用和一般模式的区别,而输出无这样的的区别。
	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);//发送引脚
	//配置TX引脚对于的管脚为上拉输入或者浮空输入
	//why  串口默认输出高电平,读取数据是,空闲状态和数据起始位都为低电平。
	gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, GPIO_PIN_10);//接受引脚
}

static void Usb2ComUartInit(uint32_t baudRate)
{
	/* 使能UART时钟;*/
	rcu_periph_clock_enable(RCU_USART0);
	
	/* 复位UART;*/
	usart_deinit (USART0);
	
	/* 通过USART_CTL0寄存器的WL设置字长;*/ 
	/* 初始默认WL字长为8bit*/ 
	usart_word_length_set(USART0, USART_WL_8BIT);
	
	/* 通过USART_CTL0寄存器的PCEN设置校验位;*/
	/* 初始默认无校验位*/
	usart_parity_config(USART0, USART_PM_NONE);
	
	/* 在USART_CTL1寄存器中写STB[1:0]位来设置停止位的长度;*/ 
	/*初始默认停止位1bit  可选0.5 1 1.5 2*/ 
	usart_stop_bit_set(USART0, USART_STB_1BIT);
	
	/* 在USART_BAUD寄存器中设置波特率;*/ 
	usart_baudrate_set(USART0, baudRate);
	
	/* 在USART_CTL0寄存器中设置TEN位,使能发送功能;*/
	usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
	
	/* 在USART_CTL0寄存器中置位UEN位,使能UART;*/ 
	usart_enable(USART0);
}

void Usb2ComTest(void)
{
	for (uint8_t i = 0; i <= 250; i++)
	{
		usart_data_transmit(USART0, i);//发送数据
		while (RESET == usart_flag_get(USART0, USART_FLAG_TBE));  
		//当TBE为0,等待,当TBE为0时,表示TXR数据寄存器有数据,需要继续等待
		//当TBE为1,跳出循环,表示TXR数据寄存器无数据,可以继续向位移寄存器下一个数据。
		总结:RENE和TBE标志位为1是都表示已经准备好的意思,可以开始接受和发送下一个数据。TC为1时表示数据发送完毕。
	}
//	while (RESET == usart_flag_get(g_uartHwInfo.uartNo, USART_FLAG_TC));
//	usart_transmit_config(g_uartHwInfo.uartNo, USART_TRANSMIT_DISABLE);
	//TC为1时,跳出循环,表示位移寄存器数据(无数据),即发送完毕。
	//TC为0时,等待,表示位移寄存器数据有数据,需要继续等待。
}

/**
***********************************************************
* @brief USB转串口硬件初始化
* @param
* @return 
***********************************************************
*/
void Usb2ComDrvInit(void)
{
	Usb2ComGpioInit();
	Usb2ComUartInit(115200);
}

/**
***********************************************************
* @brief printf函数默认打印输出到显示器,如果要输出到串口,
		 必须重新实现fputc函数,将输出指向串口,称为重定向
* @param
* @return 
***********************************************************
*/
int fputc(int ch, FILE *f)
{
	usart_data_transmit(USART0, (uint8_t)ch);
	while (RESET == usart_flag_get(USART0, USART_FLAG_TBE));
	return ch;
}

五、USART接收数据示例代码

main文件
#include <stdint.h>
#include <stdio.h>
#include "gd32f30x.h"
#include "led_drv.h"
#include "key_drv.h"
#include "systick.h"
#include "usb2com_drv.h"

int main(void)
{	
	SystickInit();
	LedDrvInit();
	KeyDrvInit();
	Usb2ComDrvInit();
	
	while (1)
	{
		Usb2ComTask();
	}
}
usb2com_drv.c
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "gd32f30x.h"
#include "led_drv.h"

typedef struct
{
	uint32_t uartNo;
	rcu_periph_enum rcuUart;
	rcu_periph_enum rcuGpio;
	uint32_t gpio;
	uint32_t txPin;
	uint32_t rxPin;
	uint8_t irq;
} UartHwInfo_t;

static UartHwInfo_t g_uartHwInfo = {USART0, RCU_USART0, RCU_GPIOA, GPIOA, GPIO_PIN_9, GPIO_PIN_10, USART0_IRQn};

static void Usb2ComGpioInit(void)
{
	rcu_periph_clock_enable(g_uartHwInfo.rcuGpio);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, g_uartHwInfo.txPin);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, g_uartHwInfo.rxPin);
}

static void Usb2ComUartInit(uint32_t baudRate)
{
	/* 使能UART时钟;*/
	rcu_periph_clock_enable(g_uartHwInfo.rcuUart);
	/* 复位UART;*/
	usart_deinit (g_uartHwInfo.uartNo);
	/* 通过USART_CTL0寄存器的WL设置字长;*/ 
	//usart_word_length_set(g_uartHwInfo.uartNo, USART_WL_8BIT);
	/* 通过USART_CTL0寄存器的PCEN设置校验位;*/ 
	//usart_parity_config(g_uartHwInfo.uartNo, USART_PM_NONE);
	/* 在USART_CTL1寄存器中写STB[1:0]位来设置停止位的长度;*/ 
	//usart_stop_bit_set(g_uartHwInfo.uartNo, USART_STB_1BIT);
	/* 在USART_BAUD寄存器中设置波特率;*/ 
	usart_baudrate_set(g_uartHwInfo.uartNo, baudRate);
	/* 在USART_CTL0寄存器中设置TEN位,使能发送功能;*/
	usart_transmit_config(g_uartHwInfo.uartNo, USART_TRANSMIT_ENABLE);
	/* 在USART_CTL0寄存器中设置TEN位,使能接收功能;*/
	usart_receive_config(g_uartHwInfo.uartNo, USART_RECEIVE_ENABLE);
	/* 使能串口接收中断;*/
	usart_interrupt_enable(g_uartHwInfo.uartNo, USART_INT_RBNE);
	/* 使能串口中断;*/
	nvic_irq_enable(g_uartHwInfo.irq, 0, 0);
	/* 在USART_CTL0寄存器中置位UEN位,使能UART;*/ 
	usart_enable(g_uartHwInfo.uartNo);
}

/**
***********************************************************
* @brief USB转串口硬件初始化
* @param
* @return 
***********************************************************
*/
void Usb2ComDrvInit(void)
{
	Usb2ComGpioInit();
	Usb2ComUartInit(115200);
}

/**
***********************************************************************
包格式:帧头0  帧头1  数据长度  功能字   LED编号  亮/灭  异或校验数据
        0x55   0xAA    0x03      0x06     0x00     0x01      0xFB
***********************************************************************
*/
#define FRAME_HEAD_0        0x55  
#define FRAME_HEAD_1        0xAA
#define CTRL_DATA_LEN       3     //数据域长度
#define PACKET_DATA_LEN     (CTRL_DATA_LEN + 4)  //包长度
#define FUNC_DATA_IDX       3     //功能字数组下标
#define LED_CTRL_CODE       0x06  //功能字

#define MAX_BUF_SIZE 20
static uint8_t g_rcvDataBuf[MAX_BUF_SIZE];//用于存储一包数据  7个字节
static bool g_pktRcvd = false;//接受一包数据的标志位

typedef struct
{
	uint8_t ledNo;
	uint8_t ledState;
} LedCtrlInfo_t;

static void ProcUartData(uint8_t data)
{
	//函数出作用域后不会销毁
	//初始化也只有一次
	static uint8_t index = 0;
	
	//初始化也只有一次
	
	//这个在循环里面,
	//只要是正常数据,就会存入一次,同时index增加一次
	
	g_rcvDataBuf[index++] = data;
	
	switch (index)
	{
		case 1:
			//判断第一个接受的数据是不是第一个针头 
			//是针头则会,重新开始接受
			if (g_rcvDataBuf[0] != FRAME_HEAD_0)
			{
				index = 0;//置为初始
			}
			break;
			//判断第一个接受的数据是不是第一个针头
		case 2:
			if (g_rcvDataBuf[1] != FRAME_HEAD_1)
			{
				index = 0;
			}
			break;
			
		//PACKET_DATA_LEN为7,表示接受到7个字符
		case PACKET_DATA_LEN:
			g_pktRcvd = true;
		
			//置为0,等待接受下一个数据包
			index = 0;
			break;
		default:
			break;
	}
}

/**
***********************************************************
* @brief 对数据进行异或运算
* @param data, 存储数组的首地址
* @param len, 要计算的元素的个数
* @return 异或运算结果
***********************************************************
*/
static uint8_t CalXorSum(const uint8_t *data, uint32_t len)
{
	uint8_t xorSum = 0;
	for (uint32_t i = 0; i < len; i++)
	{
		xorSum ^= data[i];
	}
	return xorSum;
}

/**
***********************************************************
* @brief LED控制处理函数
* @param ctrlData,结构体指针,传入LED的编号和状态
* @return 
***********************************************************
*/
static void CtrlLed(LedCtrlInfo_t *ctrlData)
{
	ctrlData->ledState != 0 ? TurnOnLed(ctrlData->ledNo) : TurnOffLed(ctrlData->ledNo);
}

/**
***********************************************************
* @brief USB转串口任务处理函数  即数据解析
* @param
* @return 
***********************************************************
*/
void Usb2ComTask(void)//while内部,会反复执行该函数。return和break类似
{
	if (!g_pktRcvd)
	{
		return;
	}
	
	//如果为真,在传输数据之后,也需要把right_data_flag置为false。
	g_pktRcvd = false;
	
	if (CalXorSum(g_rcvDataBuf, PACKET_DATA_LEN - 1) != g_rcvDataBuf[PACKET_DATA_LEN - 1])
	{
		return;
	}
	
	//获取一包数据的功能数据[3]
	if (g_rcvDataBuf[FUNC_DATA_IDX] == LED_CTRL_CODE)
	{
		//LED编号和状态二个字节传输给他
		CtrlLed((LedCtrlInfo_t *)(&g_rcvDataBuf[FUNC_DATA_IDX + 1]));
	}
}

/**
***********************************************************
* @brief 串口0中断服务函数
* @param
* @return 
***********************************************************
*/
void USART0_IRQHandler(void)
{
	//接受数据后,产生中断才进入该if语句
	if (usart_interrupt_flag_get(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
	{
		usart_interrupt_flag_clear(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE);
		
		//接受数据
		uint8_t uData = (uint8_t)usart_data_receive(g_uartHwInfo.uartNo);
		
		// 然后就读取数据内容了
		//需要定义一个处理接收到的串口数据了
		ProcUartData(uData);
	}
}

/**
***********************************************************
* @brief printf函数默认打印输出到显示器,如果要输出到串口,
		 必须重新实现fputc函数,将输出指向串口,称为重定向
* @param
* @return 
***********************************************************
*/
int fputc(int ch, FILE *f)
{
	usart_data_transmit(g_uartHwInfo.uartNo, (uint8_t)ch);
	while (RESET == usart_flag_get(g_uartHwInfo.uartNo, USART_FLAG_TBE));
	return ch;
}

六、USART常用的库函数

注意:
开启中断是才采用usart_interrupt_flag_get()获取中断标志位
未开启中断,采用usart_flag_get()获取标志位。

未开启中断,usart_interrupt_flag_get()获取的值一直为0。

usart_deinit():复位外设USART

void usart_deinit(uint32_t usart_periph)
usart_periph:USART0~3/UART3~4
/* reset USART0 */
usart_deinit (USART0);

usart_baudrate_set():配置USART波特率

void usart_baudrate_set(uint32_t usart_periph, uint32_t baudval);
/* configure USART0 baud rate value */
usart_baudrate_set(USART0, 115200);

usart_parity_config():配置USART奇偶校验

void usart_parity_config(uint32_t usart_periph, uint32_t paritycfg);
paritycfg(配置USART奇偶校验):USART_PM_NONE(无校验)USART_PM_ODD(奇校验)USART_PM_EVEN(偶校验)
/* configure USART parity */
usart_parity_config(USART0, USART_PM_EVEN);

usart_word_length_set():配置USART字长

void usart_word_length_set(uint32_t usart_periph, uint32_t wlen);
wlen (配置USART字长):USART_WL_8BIT (8 bits )  USART_WL_9BIT(9 bits) 
/* configure USART0 word length */
usart_word_length_set(USART0, USART_WL_9BIT);

usart_stop_bit_set():配置USART停止位

void usart_stop_bit_set(uint32_t usart_periph, uint32_t stblen);
/* configure USART0 stop bit length */
usart_stop_bit_set(USART0, USART_STB_1_5BIT);

在这里插入图片描述

usart_enable():使能USART

void usart_enable(uint32_t usart_periph);
/* enable USART0 */
usart_enable(USART0);

usart_disable():失能USART

void usart_disable(uint32_t usart_periph);
/* disable USART0 */
usart_disable(USART0);

usart_transmit_config():USART发送配置

void usart_transmit_config(uint32_t usart_periph, uint32_t txconfig);
txconfig(使能/失能USART发送器):
USART_TRANSMIT  使能USART发送   USART_TRANSMIT_DISABLE_ENABLE  失能USART发送
/* configure USART0 transmitter */
usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);

usart_receive_config():USART接收配置

void usart_receive_config(uint32_t usart_periph, uint32_t rxconfig);
rxconfig使能/失能USART接收器:USART_RECEIVE_ENABLE 使能USART接收  
USART_RECEIVE_DISABLE  失能USART接收
/* configure USART0 receiver */
usart_receive_config(USART0, USART_RECEIVE_ENABLE);

usart_data_transmit():USART发送数据功能

void usart_data_transmit(uint32_t usart_periph, uint32_t data);
/* USART0 transmit data */
usart_data_transmit(USART0, 0xAA);

usart_data_receive():USART接收数据功能

uint16_t usart_data_receive(uint32_t usart_periph);
uint16_t temp;
temp = usart_data_receive(USART0);

usart_flag_get():获取USART状态寄存器标志位

FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag);
usart_flag_enum flag:USART状态寄存器标志位  flag 表示标志为
/* get flag USART0 state */
FlagStatus status;
status = usart_flag_get(USART0,USART_FLAG_TBE);

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

usart_flag_clear():清除USART状态寄存器标志位

void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag);
例如:
/* clear USART0 flag */
usart_flag_clear(USART0,USART_FLAG_TC);

usart_interrupt_enable():使能USART中断

void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum 
interrupt)
/* enable USART0TBE interrupt */
usart_interrupt_enable(USART0, USART_INT_TBE);

在这里插入图片描述

usart_interrupt_disable():失能USART中断

void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum 
interrupt);

/* disable USART0TBE interrupt */
usart_interrupt_disable(USART0, USART_INT_TBE);

usart_interrupt_flag_get():获取USART中断标志位状态

FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag);
/* get the USART0 interrupt flag status */
FlagStatus status;
status = usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE);

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

usart_interrupt_flag_clear():清除USART中断标志位状态

void usart_interrupt_flag_clear(uint32_t usart_periph, 
usart_interrupt_flag_enum int_flag);
/* clear the USART0 interrupt flag */
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值