江科大stm32+keil5第九章USATRT串口

一、串口发送

1.1主要函数

void Serial_Init(void);//初始化函数
void Serial_SendByte(uint8_t Byte);//数据发送函数

根据不同数据类型可以封装多个函数用于发送不同类型数据至串口,但主要还是使用USART_SendData函数发送字节。

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStruct;	
	USART_InitStruct.USART_BaudRate = 9600;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = 1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;		
	USART_Init(USART1,&USART_InitStruct);
	
	USART_Cmd(USART1,ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1,Byte);
	//如果状态为RESET,表示发送未完成,循环会继续等待;如果状态非RESET,表示发送已完成,循环会退出。
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);	//等待发送完成
}

1.2不同类型发送韩函数

//数组
void Serial_SendArray(uint8_t *Array,uint16_t Length)
{
	uint16_t i;
	for(i=0;i<Length;i++)
	{
		Serial_SendByte(Array[i]);
	}
}
//字符串
void Serial_SendString(char *String)
{
	uint8_t i;
	for(i = 0;String[i] != 0;i++)
	{
		Serial_SendByte(String[i]);
	
	}	
}
//数字,根据除余获取数字个十百千位
uint32_t Serial_Pow(uint32_t X,uint32_t Y)
{
	uint32_t Result = 1;
	while(Y--)
	{
	Result *=X;
	}
	return Result;
}
void Serial_SendNumber(uint32_t Number,uint8_t Length)
{
	uint8_t i;
	for(i = 0;i < Length;i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10,Length -i -1) % 10 + '0');// '0' 表示ASCll码偏移,正好偏移到数字位置	
	}
}
//printf打印调用fput,重定向printf使其也打印到串口上
int fputc(int ch,FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}
//将传入的格式化字符串和参数列表进行格式化处理,并将结果发送到串口。
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(String,format,arg);
	va_end(arg);
	Serial_SendString(String);
}
//main函数

	Serial_Init();
	
	Serial_SendByte(0x41);
	
   uint8_t MyArray[] = {0x42, 0x43, 0x44, 0x45};
	Serial_SendArray(MyArray, 4);
	
	Serial_SendString("\r\nNum1=");
	Serial_SendNumber(111,3);
	printf("\r\nNum2=%d",222);
	char String[100];
	sprintf(String,"\r\nNum=3%d",333);
	Serial_SendString(String);
	Serial_Printf("\r\nNum4=%d",444);
	Serial_Printf("\r\n");

		while (1)
	{
		
	}

1.3注意事项

(1)printf需打开keil5 Use MicroLIB选项(是keil为嵌入式平台优化的一个精简库)
(2)printf函数重定向,printf默认输出到屏幕,单片机没有屏幕,重定向至串口

int fputc(int ch,FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

(3)printf打印中文
1) keil与串口都选取UTF8,并且在keil5 c/c++ 杂项控制栏写入,“–no-multibyte-chars”。
2) 切换GB2312编码格式。
3) 修改编码格式之后需要删除汉字,重新打开文件。
4)keil6则只需修改串口接受方式为UTF8即可。

二、串口发送和接收

2.1简单介绍

需要两个GPIO口pin9和pin10,pin9选择复用推挽输出,pin10选择上拉输入。
接收分为查询接收和中断接收

2.1.1查询

在while函数中查看标志位,
//用于检查USART1的接收数据寄存器是否非空(即接收到了新的数据),如果非空,则读取接收到的数据并显示在OLED屏幕上。
		if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
		{
			RxData = USART_ReceiveData(USART1);
			OLED_ShowHexNum(1,1,RxData,2);
		}

2.1.2中断

下述代码仅能接收转存单字节

中断函数配置
	//配置USART1的中断和优先级。
	//启用USART1的接收数据寄存器非空中断(RXNE).当接收到新的数据时,将触发该中断。
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	//中断通道设置为USART1的中断号(USART1_IRQn)。
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
串口接收发送
//获取接收标志位的状态。
//如果接收标志位为1,表示接收到了新的数据,将接收标志位清零并返回1;否则返回0。
//读后自动清除标志位
uint8_t Serial_GetRxFlag(void)
	{
		if(Serial_RxFlag == 1)
		{
			Serial_RxFlag = 0;
			return 1;		
		}
		return 0;
	}
	
//获取接收到的数据。
uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

//读取USART_IT_RXNE标志位会自动清除标志位,下面函数采用手动清除
void USART1_IRQHandler(void)
{
	//检查接收数据寄存器是否非空
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET)
	{

		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		//清除接收数据寄存器的中断标志位
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	}
}

main函数,较为简单


		if(Serial_GetRxFlag() == 1)
		{
		//读取接收到的数据,并将其存储在变量RxData中。
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1,1,RxData,2);
		}

三、串口收发HEX数据包

3.1关于main文件获取其他文件和static静态变量

main文件获取其他文件

	seiial.c文件
uint8_t Serial_RxData;

	uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

	main.c文件
	
			if(Serial_GetRxFlag() == 1)
		{
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1,9,RxData,2);
		}

	获取了Serial_RxData的数值,但是较为麻烦,需要封装函数。

	善用extern函数
	serial.h文件
	extern uint8_t Serial_RxData;
	
	seiial.c文件
	uint8_t Serial_RxData;
	
	main.c文件
	
			if(Serial_GetRxFlag() == 1)
		{
			Serial_SendByte(Serial_RxData);
			OLED_ShowHexNum(1,9,Serial_RxData,2);
		}

static静态变量
static静态变量相当于全局变量,函数进入只会初始化一次,在函数退出后,数据仍然有效。
与全局变量不同的是静态变量只能在本函数使用。
HEX收数据包和文本收数据包的状态变量S适用静态变量。

3.2收发送数据包

3.2.1收发送固定包长HEX数据包

发送数据包

Serial.c
uint8_t Serial_RxPacket[4];
void Serial_SendPacket()
{
	Serial_SendByte(0xFF);
	Serial_SendArray(Serial_TxPacket,4);
	Serial_SendByte(0xFE);
}
Serial.h
外部可调用
extern uint8_t Serial_RxPacket[];
main.c
	Serial_TxPacket[0] = 0x01;
	Serial_TxPacket[1] = 0x02;
	Serial_TxPacket[2] = 0x03;
	Serial_TxPacket[3] = 0x04;
	
	while (1)
	{
		KeyNum = Key_GetNum();
		if (KeyNum == 1)
		{
			Serial_TxPacket[0] ++;
			Serial_TxPacket[1] ++;
			Serial_TxPacket[2] ++;
			Serial_TxPacket[3] ++;
			
			Serial_SendPacket();
			
			OLED_ShowHexNum(2, 1, Serial_TxPacket[0], 2);
			OLED_ShowHexNum(2, 4, Serial_TxPacket[1], 2);
			OLED_ShowHexNum(2, 7, Serial_TxPacket[2], 2);
			OLED_ShowHexNum(2, 10, Serial_TxPacket[3], 2);
			
		}
		if(Serial_GetRxFlag() == 1)
		{
			//Serial_RxPacket 既是写入又是读出,数据包之间可能混在一起
			//解决方法1.每个数据包读取完毕之后,再接收下一个数据包
			//2.HEX数据包多用于接收连续性数据包,可以不用关心
			OLED_ShowHexNum(4, 1, Serial_RxPacket[0], 2);
			OLED_ShowHexNum(4, 4, Serial_RxPacket[1], 2);
			OLED_ShowHexNum(4, 7, Serial_RxPacket[2], 2);
			OLED_ShowHexNum(4, 10, Serial_RxPacket[3], 2);
		}
	}

接收数据包

//获取接收标志位的状态。
//如果接收标志位为1,表示接收到了新的数据,将接收标志位清零并返回1;否则返回0。
//读后自动清除标志位
uint8_t Serial_GetRxFlag(void)
	{
		if(Serial_RxFlag == 1)
		{
			Serial_RxFlag = 0;
			return 1;		
		}
		return 0;
	}
//读取USART_IT_RXNE标志位会自动清除标志位,下面函数采用手动清除
void USART1_IRQHandler(void)
{	
	static uint8_t RxState = 0;//状态变量S
	static uint8_t pRxPacket = 0;//数组第i位
	
	//检查接收数据寄存器是否非空
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET)
	{
		//返回USARTx外设最近接收到的数据
		uint8_t  RxData = USART_ReceiveData(USART1);
		//else if只能选择执行一个状态代码
		//等待包头
		if(RxState == 0)
		{
			if(RxData == 0xFF)	//收到包头
			{
				RxState = 1;
				pRxPacket = 0;//pRxPacke初始化
			}
		}
		//接收数据
		else if(RxState == 1)
		{
			Serial_RxPacket[pRxPacket] = RxData;
			pRxPacket++;//数组位数+1
			if(pRxPacket >=4)
			{
				RxState = 2;//固定字节数据接收完毕,变换状态位
			}
		}
		//等待包尾
		else if (RxState == 2)
		{
			if (RxData == 0xFE)
			{
				RxState = 0;
				Serial_RxFlag = 1;
			}
		}
		//清除接收数据寄存器的中断标志位
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	}
}

3.2.2 接收可变步长文本数据包

注意状态标志位=RxState 和 数据接收RxData 不能混淆
Serial.c
void USART1_IRQHandler(void)
{	
	static uint8_t RxState = 0;//状态变量S
	static uint8_t pRxPacket = 0;//数组第i位
	
	//检查接收数据寄存器是否非空
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE) == SET)
	{
		//返回USARTx外设最近接收到的数据
		uint8_t RxData = USART_ReceiveData(USART1);
		//else if只能选择执行一个状态代码
		//等待包头
		if(RxState == 0)
		{
			if(RxData == '@' && Serial_RxFlag == 0)	//收到包头
			{
				RxState = 1;
				pRxPacket = 0;//pRxPacke初始化
			}
		}
		//接收数据
		
		else if (RxState == 1)
		{
			if (RxData == '\r')
			{
				RxState = 2;
			}
			else
			{
				Serial_RxPacket[pRxPacket] = RxData;
				pRxPacket++;//数组位数+1		
			}
		}
		//等待包尾
		else if (RxState == 2 )
		{
			if (RxData == '\n')
			{
				RxState = 0;
				Serial_RxPacket[pRxPacket] = '\0';
				Serial_RxFlag = 1;
			}
		}
		//清除接收数据寄存器的中断标志位
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	}
}

3.2.3 根据输入内容控制其他外设

控制LED亮灭
while (1)
	{
		if(Serial_RxFlag == 1)
		{
			OLED_ShowString(4, 1, "                ");
			OLED_ShowString(4, 1, Serial_RxPacket);
			
			if (strcmp(Serial_RxPacket, "LED_ON") == 0)
			{
				LED1_ON();
				Serial_SendString("LED_ON_OK\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_ON_OK");
			}
			else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
			{
				LED1_OFF();
				Serial_SendString("LED_OFF_OK\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "LED_OFF_OK");
			}
			else
			{
				Serial_SendString("ERROR_COMMAND\r\n");
				OLED_ShowString(2, 1, "                ");
				OLED_ShowString(2, 1, "ERROR_COMMAND");				
			}
		Serial_RxFlag = 0;
		}
	}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值