STM32串口发送接收完结(串口发送、串口回复、根据指定完成点灯、问答功能)

前言

提示:这里可以添加本文要记录的大概内容:

目前已经学习了GPIO的输入输出,但是没有完整的显示信息,最便宜的显示就是串口。

000 -111

AVR单片机 已经学会过了,


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

一、串口是什么?

1.串口的基本认识

https://www.bilibili.com/video/BV1y34y147s5/?spm_id_from=333.337.search-card.all.click
https://www.bilibili.com/video/BV1PD4y147ts/?spm_id_from=autoNext
https://www.bilibili.com/video/BV14k4y187e6/?spm_id_from=autoNext
在这里插入图片描述

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

在这里插入图片描述

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

目标
在这里插入图片描述

2.STM32的串口结构

  1. 对外的就是TX RX
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
  2. 读写原理在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

USART_DR类似51单片机的SBUF

3.实现串口通信最简单的就是配置好和串口助手参数

二、使用步骤

1.开启时钟

  • GPIO的时钟
  • UART
    代码如下(示例):
	//开启串口1和GPIOA.9  10的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_USART1, ENABLE);

2. 配置引脚

  • 串口1 PA9 PA10
    在这里插入图片描述
    在这里插入图片描述
	//2.配置GPIO
	
	GPIO_InitStruct.GPIO_Pin=  GPIO_Pin_9;          //TX
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);   //&x
	
	GPIO_InitStruct.GPIO_Pin=  GPIO_Pin_10;         //RX
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);   //&x


3.初始化串口

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

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

代码如下(示例):

	// 3.配置USART
	
	USART_InitStruct.USART_BaudRate=  9600;
	USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStruct.USART_Parity=  USART_Parity_No;
	USART_InitStruct.USART_StopBits= USART_StopBits_1;
	USART_InitStruct.USART_WordLength=  USART_WordLength_8b;
	
	USART_Init( USART1, &USART_InitStruct);

4. 串口使能

在这里插入图片描述

USART_Cmd(USART1, ENABLE);

5. 串口如何发送?

在这里插入图片描述

5.1发送单个字符

USART_SendData(USART1, 'A');

5.2 发送多个字符

// 5. 发送	
	USART_SendData(USART1, 'A');
	//加入等待发送完成
	// 没发送完成就一直是0,发送完成就是1
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	//while(!USART_GetFlagStatus(USART1, USART_FLAG_TC));
	//while(!USART_GetFlagStatus(USART1, USART_FLAG_TC));
	
	USART_SendData(USART1, 'B');
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	USART_SendData(USART1, 'C');
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	USART_SendData(USART1, 'D');
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	USART_SendData(USART1, 'E');
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	USART_SendData(USART1, 'F');
	while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));

在这里插入图片描述

5.3 写成一个发送字符串的函数

字符串最后一位就是 0,NULL


U8 ta[]="123";

send_string(ta); //这时候ta代表装着 123数据的首地址,1的地址,*p就是1,p就是1的地址也首地址,ta;
 void send_string(u8 *p)
{
	
	    while(*p !=0)
		{
			USART_SendData(USART1, *p);
			while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
			p++;
		}	
}

5.4 发送多类型,采用printf()

需要重定向一个函数,让printf 发送到串口而不是C语言里面的控制台
加头文件
#include “stdio.h”

int fputc(int ch,FILE *f)
{

	   USART_SendData(USART1, (u8)ch);
	   while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE));
	   return ch;
}

单独写这个不能发送
要勾选

在这里插入图片描述

printf("STM32简答吗?");
printf("STM32简答吗?\r\n");

printf("STM32简答吗?能考%d 分?\r\n",60);

在这里插入图片描述

6. 串口如何接收?

串口中断数据接收有2种方法,为了节省资源,一般采用中断的方法

  1. 查询
  2. 中断

6.1 查询的方法,在主函数不断获取标志位,

 void main()
 {
	while(1)
	{
		if(串口接收完成)
		{
			temp= 接收函数
		}
	}
}

6.2 中断获取数据的方法

  • 步骤
  1. 中断分组
  2. 配置NVIC优先级管理
  3. 开启串口接收中断
  4. 写中断服务函数
    第一、二步
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd= ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=  1;
	NVIC_Init(&NVIC_InitStruct);

第三步

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

USART_ITConfig( USART1, USART_IT_RXNE, ENABLE);

第四步

void USART1_IRQHandler()   //中断函数
{
	u16 temp;
	if(USART_GetITStatus( USART1, USART_IT_RXNE))  //判断是否是接收完成中断
	{
	
	     temp=USART_ReceiveData(USART1);      //接收数据
          USART_SendData(USART1, temp);       //发送收据
		
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清楚接收完成中断
		
		
	}
}

7.是否要中断?要中断有什么好处

三、测试

1. 硬件条件

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

2. 测试成功发送了一个字符

在这里插入图片描述

3.发送多个字符

在这里插入图片描述

如何解决问题

在这里插入图片描述

在这里插入图片描述
解决方法:

在这里插入图片描述
成功的案列
在这里插入图片描述

四、接收字符串比较字符串

使用串口交互代码问题

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "USART.h"
#include "LED.h"
#include "string.h" 
#include "stdio.h" 

uint8_t Data;//接收一字节数据
unsigned char RX_Str[100];
unsigned char ta[100]={"1234我的名字"};
u16 Rx_FLAG=0; //接收完成标志位 

 unsigned char hongdeng[]={"红灯"};
int main()
{
	USART_INT();//串口初始化
	LED_int();//LED初始化
	
	USART_SendData(USART1,'a');//串口能正确接收说明硬件、软件没问题
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	printf("你好吗\r\n");//打印红灯亮信息 红灯亮   \r\n换行
	//串口能正确发送说明 发送编码没问题
	while(1)
	{
		if(Rx_FLAG==1)//接收完成标志 Rx_FLAG 为 1  
		{
			if(strcmp(RX_Str,"红灯")==0)//比对接收的字符是否为 红灯 
			{
				LEDR_ON();//红灯亮
				printf("红灯亮 \r\n");//打印红灯亮信息 红灯亮   \r\n换行
			}
			else if(strcmp(RX_Str,"蓝灯")==0)//比对接收的字符是否为 蓝灯 
			{
				LEDB_ON();//蓝灯亮
				printf("蓝灯亮 \r\n");//打印蓝灯亮信息 蓝灯亮
			}
			else if(strcmp(RX_Str,"绿灯")==0)//比对接收的字符是否为 绿灯
			{
				LEDG_ON();//绿灯亮
				printf("绿灯亮 \r\n");//打印绿灯亮信息 绿灯亮
			}
			else if(strcmp(RX_Str,"关灯")==0)//比对接收的字符是否为 关灯
			{
				LED_ALLOFF();//灯全灭
				printf("已关灯 \r\n");//打印灯灭信息 已关灯
			}
			else if(strcmp(RX_Str,"我的学号")==0)//比对接收的字符是否为  我的学号
			{
				printf("学号:2015252525225 \r\n");//打印  学号:2015252525225
			}
			else if(strcmp(RX_Str,"我的姓名")==0)//比对接收的字符是否为  我的姓名
			{
				printf("姓名:xxx \r\n");//打印 
			}
			else  //若输入指令都不符合 则打印输入指令错误
			{
				printf("输入指令错误!! \r\n");//打印输入指令错误
			}
			Rx_FLAG=0;//接收完成标志位置0  
		}
	}
}


/*
通过接收HEX数据包实现交互   包头@ 数据1 数据2 ... 包尾\r\n
利用状态转换思想接收HEX数据包  相当于数电的状态转换 等待转换信号来临进而转换状态
*/
void USART1_IRQHandler()    //串口中断函数
{
	static u16 RX_zt;//串口接收状态
	static u16 i;//字符数组下标
	if (USART_GetITStatus(USART1, USART_IT_RXNE)==SET)//当接收标志位为SET 开始接收
	{
		Data=USART_ReceiveData(USART1);//接收数据
		if(RX_zt==0)//若串口接收状态为状态0
		{
			if(Data==' '&&Rx_FLAG==0)//判断接收到的数据是否为包头 @ 在Rx_FLAG=0情况下接收数据防止数据传输过快错乱
			{
				RX_zt=1;//转换为状态1
				i=0;//字符数组下标置0
			}
		}
		// 123132\r\n
		else if(RX_zt==1)//若串口接收状态为状态1
		{
			if(Data=='\r')//判断是否为包尾字符 不定长HEX数据包接收数据需时刻比对包尾信号
			{
				RX_zt=2;//转换为状态2
			}
			else
			{
				RX_Str[i]=Data;//接收数据存入字符数组
				i++;
			}
		}
		else if(RX_zt==2)//若串口接收状态为状态2
		{
			if(Data=='\n')//再次判断是否为包尾字符
			{
				RX_zt=0;//转换为状态0
				RX_Str[i]='\0';//字符串接收结束
				Rx_FLAG=1;//接收完成标志位置1
			}
		}
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接收标志位
	}
}

strcmp函数讲解在这里插入图片描述

https://baike.baidu.com/item/strcmp/5495571?fr=ge_ala

4.0 发送无回应如何判断

  • 1.自己声明一个字符串,然后硬件仿真能 发现显示“红灯” 得到编码错误

在这里插入图片描述

    1. 接收字符串回来数组里面得到数据位 “红灯”显示正确
      在这里插入图片描述
    1. 核心问题:32用的是UTF-8编码 ->上位机用的是ANSI编码

4.1 老铁给出的错误分析很好

https://blog.csdn.net/qq_44852376/article/details/128460223

核心问题:32用的是UTF-8编码 ->上位机用的是ANSI编码

4.2 怎么解决?

4.2.1 新建工程重来

新建工程,一开始就用ANSI编码,中间不要随意改动编码

4.2.2 强制编码,改回来

使用nopepad++软件实现

在这里插入图片描述

在这里插入图片描述

总结

实现对话,发送名字,学号,指定三盏灯的状态,
最有加入了 发送和回复的功能,为上位机 通信奠定了基础!

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以帮你实现一个基于 STM32 的数字钟,通过串口发送当前间。首先,你需要准备一块 STM32 开发板和一个带有串口功能的显示屏。 下面是实现步骤: 1. 配置 STM32钟和串口,使其能够正常工作。 2. 编写代码,获取当前间,并将其转换为字符串格式。 3. 使用串口发送字符串格式的当前间到显示屏上。 以下是示例代码,你可以参考一下: ```c #include "stm32f10x.h" #include "stdio.h" #include "string.h" #define USART USART1 #define BAUDRATE 9600 void init_USART(void); void USART_send(uint16_t ch); void USART_puts(char *str); void init_clock(void); void init_GPIO(void); void init_timer(void); void RTC_Configuration(void); void delay_ms(uint16_t ms); RTC_TimeTypeDef RTC_TimeStruct; RTC_DateTypeDef RTC_DateStruct; int main(void) { char time_str[20]; init_clock(); init_GPIO(); init_USART(); init_timer(); RTC_Configuration(); while(1) { RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct); RTC_GetDate(RTC_Format_BIN,&RTC_DateStruct); sprintf(time_str, "%02d:%02d:%02d", RTC_TimeStruct.RTC_Hours, RTC_TimeStruct.RTC_Minutes, RTC_TimeStruct.RTC_Seconds); USART_puts(time_str); USART_puts("\r\n"); delay_ms(1000); } } void init_USART(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; //使能USART和GPIO的钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //USART1_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //USART初始化 USART_InitStructure.USART_BaudRate = BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART, &USART_InitStructure); //使能USART USART_Cmd(USART, ENABLE); } void USART_send(uint16_t ch) { while (USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET); USART_SendData(USART, (uint8_t)ch); } void USART_puts(char *str) { while(*str) { USART_send(*str); str++; } } void init_clock(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); RCC_WaitForHSEStartUp(); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); SysTick_Config(SystemCoreClock / 1000); } void init_GPIO(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } void init_timer(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_BackupAccessCmd(ENABLE); RCC_LSEConfig(RCC_LSE_ON); while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); } void RTC_Configuration(void) { RTC_InitTypeDef RTC_InitStructure; //使能PWR和BKP的钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能RTC PWR_BackupAccessCmd(ENABLE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); //RTC初始化 RTC_InitStructure.RTC_AsynchPrediv = 0x7F; RTC_InitStructure.RTC_SynchPrediv = 0xFF; RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; RTC_Init(&RTC_InitStructure); //设置间 RTC_TimeTypeDef RTC_TimeStruct; RTC_DateTypeDef RTC_DateStruct; RTC_TimeStruct.RTC_Hours = 0x12; RTC_TimeStruct.RTC_Minutes = 0x00; RTC_TimeStruct.RTC_Seconds = 0x00; RTC_SetTime(RTC_Format_BIN,&RTC_TimeStruct); RTC_DateStruct.RTC_WeekDay = RTC_Weekday_Monday; RTC_DateStruct.RTC_Month = RTC_Month_January; RTC_DateStruct.RTC_Date = 0x01; RTC_DateStruct.RTC_Year = 0x21; RTC_SetDate(RTC_Format_BIN,&RTC_DateStruct); } void delay_ms(uint16_t ms) { volatile uint32_t nCount; RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); nCount = (RCC_Clocks.HCLK_Frequency / 10000) * ms; for (; nCount != 0; nCount--); } void SysTick_Handler(void) { static uint32_t ticks = 0; ticks++; if(ticks >= 1000) { ticks = 0; GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13))); } } ``` 在这个代码中,我们使用 RTC(Real-Time Clock)模块获取当前间,并将其转换为字符串格式,然后使用 USART 发送到显示屏上。同,我们还使用 SysTick 定器来控制 LED 灯的闪烁,以证明程序在运行。 这只是一个简单的示例,你需要根据你的具体需求进行修改和完善。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值