嵌入式开发十八:USART串口通信实验
上一节我们学习了串口通信的基本理论,串口通信是学习单片机的一个重要的一步,非常重要,这一节我们通过实验来学习串口通信的使用,以及串口的接收中断的使用。
一、发送单个字节uint8_t数据或者字符型数据
实现的功能:
STM32F4 通过串口和上位机通信,发送单个字节数据(0-255)或者字符给上位机,然后显示在电脑串口助手上。
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //发送一字节函数#endif
my_usart.c文件内容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一字节(一个字符)函数
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); //这是一个库函数,用于将一个字节的数据写入USART的数据寄存器 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c代码
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();printf("发送一个字节:\n");Usart_SendByte('a');printf("\n");Usart_SendByte(97);printf("\n");while(1){}}
关键部分解读:
发送过程:调用串口发送函数USART_SendData(),但是需要保证在发送下一个字节之前,必须确保当前字节已被成功发送。否则可能会导致新数据写入时覆盖未发送完的数据,造成通信错误。因此,内部使用死循环来控制,调用库函数USART_GetFlagStatus()检查标志位来判断发送的状态!
- 这个
while
循环的作用是等待USART的数据寄存器空标志(TXE)被置位。当发送数据寄存器(TDR)中有数据时,TXE标志为RESET
。只有当TDR中的数据已被移到移位寄存器中,TXE标志才会被置位。当TXE标志为SET
时,表示发送数据寄存器已空,可以发送下一个字节- 具体来说,发送数据过程包括:
- 将数据写入TDR。
- 数据从TDR移到移位寄存器。
- 当移位寄存器开始发送数据时,TDR变空,TXE标志被置位。
while
循环确保在TDR变空之前不会发送新的数据,从而避免数据丢失或覆盖。
二、发送一个16位的数据uint16_t
实现的功能:
STM32F4 通过串口和上位机通信,发送两个字节数据(16位)给上位机,然后显示在电脑串口助手上。我们知道串口通信一次只能发送8位的数据,那么如何实现一次发16位呢?
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART_SendHalfWord(uint16_t data); //发送两个字节函数#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include <stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送两个字节(16位)数据函数
void USART_SendHalfWord(uint16_t data)
{// 分离高8位和低8位uint8_t tmp_h = data >>0x08;//将date右移8位,取得高8位数据并赋值给tmp_h。右移8位相当于将高8位移到低8位的位置,高8位的原位置被0填充。uint8_t tmp_l = data & 0xFF;//将date和0xff(255,二进制为11111111)高八位为0,进行按位与运算,取得低8位数据并赋值给tmp_l。按位与运算将高8位清零,仅保留低8位。// 发送高8位USART_SendData(USART1, tmp_h); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// 发送低8位USART_SendData(USART1, tmp_l);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();//发送两个字节的数据USART_SendHalfWord(0xffee);while(1){}}
关键部分解读:
串行通信接口通常一次只能处理8位数据,那么,我们就可以通过位运算拿到数据的高八位和低八位分别发送,
Usart_SendHalfWord
函数将16位数据分成两个8位数据(高8位和低8位),然后分别通过Usart_SendByte
函数发送出去。这种方式在串行通信中很常见。使用示例
假设我们有一个要发送的16位数据
0x1234
:
tmp_h = 0x12
:0x1234
右移8位得到高8位0x12
。tmp_l = 0x34
:0x1234
和0xff
按位与得到低8位0x34
。- 调用
Usart_SendByte(pUSARTx, 0x12)
发送高8位。- 调用
Usart_SendByte(pUSARTx, 0x34)
发送低8位。这确保16位数据能够通过支持8位传输的USART接口完整发送。
三、发送8位的数组(uint8_t)
实现的功能:
STM32F4 通过串口和上位机通信,发送一个数组(每个元素都是uint8_t类型)或者字符数组给上位机,然后显示在电脑串口助手上。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void Usart_SendByte(uint8_t date); //发送一字节函数
void USART1_SendArray(uint8_t *array, uint16_t length);//发送一个数组
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一字节函数
void Usart_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}//发送一个数组
void USART1_SendArray(uint8_t *array, uint16_t len)
{for (uint16_t i = 0; i < len; i++){Usart_SendByte(array[i]);}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要发送的字节数组uint8_t dataArray1[] = {1,2,3,4,5,6,7,8,9,10};int len =sizeof(dataArray1) / sizeof(dataArray1[0]);// 发送一个数组 USART1_SendArray(dataArray1, len);while (1){}}
四、发送字符串数据
实现的功能:
STM32F4 通过串口和上位机通信,发送字符串给电脑,然后显示在电脑串口助手上。
my_usart.h文件内容
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void My_UsartInit(void); //串口初始化
void USART1_SendString(char *str); //发送一个字符串
#endif
my_usart.c文件内容
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数第三步:GPIO 初始化设置:要设置模式为复用功能第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:使能串口*/void My_UsartInit(void)
{//1.第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//3.第三步:GPIO 初始化设置:要设置模式为复用功能GPIO_InitTypeDef Struct1;Struct1.GPIO_Mode=GPIO_Mode_AF;Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;Struct1.GPIO_Speed=GPIO_Speed_100MHz;GPIO_Init(GPIOA,&Struct1);//4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef Struct2;Struct2.USART_BaudRate=115200; //设置波特率Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位Struct2.USART_Parity=USART_Parity_No; //无校验位Struct2.USART_StopBits=USART_StopBits_1; //1位停止位Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送USART_Init(USART1,&Struct2);//5.第五步:使能串口USART_Cmd(USART1,ENABLE);}//发送一个字符串
void USART1_SendString(char *str)
{while (*str!='\0'){USART_SendData(USART1,*str);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕str++;}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c内容:
#include <stdio.h>
#include "myusart.h"int main(void)
{My_UsartInit();// 要发送的字符串char *dataString = "Hello World!";// 发送字符串printf("发送一个字符串:\n");USART1_SendString(dataString);printf("\n");while (1){ }}
五、单片机接收电脑发送的数据显示在串口助手上(接收中断)
使用串口接收中断可以让CPU在没有数据到达时执行其他任务,而不需要浪费时间轮询接收寄存器。这提高了CPU利用率和系统效率,检查标志位判断是否发生接收中断(USART_IT_RXNE
),如果是,读取接收到的数据,然后通过清除接收中断标志以准备接收下一个字节。
实现的功能:
将接收数据设置为接收中断,当上位机发送数据到STM32F407的USART1时,STM32会接收这个数据并通过中断处理函数将数据发送回上位机。上位机的串口助手会显示发送和接收的数据。
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_SendByte(uint8_t data); //发送一个字节函数
void USART1_IRQHandler(void); //中断服务函数
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include<stdio.h> //对printf()函数进行重定向引入头文件/******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。第三步:GPIO 初始化设置:要设置模式为复用功能。第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。第六步:使能串口。第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。*/void USART1_Config(void)
{//第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化设置:要设置模式为复用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;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_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:开启中断并且初始化 NVIC,使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}//发送一字节函数
void USART1_SendByte(uint8_t data)
{USART_SendData(USART1,data); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕}// 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){// 读取接收到的数据uint8_t receivedData = USART_ReceiveData(USART1);// 将接收到的数据发送回电脑USART1_SendByte(receivedData);// 清除中断标志USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
main.c
#include "myusart.h"int main(void)
{// 配置USART1USART1_Config();while (1){// 主循环中无需处理接收数据,接收数据在中断中处理}
}
六、向单片机发送指令点亮LED
实现的功能:
STM32F4 通过串口和上位机通信,发送字符串(指令): open(通过串口助手),然后单片机接收,点亮LED灯;
本实验主要是串口通信的控制功能,演示串口通信可以增强硬件的能力!
my_led..h
#ifndef __MYLED_H
#define __MYLED_Hvoid LED_Init(void);#endif
my_led.c
#include "stm32f4xx.h" // Device header
#include "myled.h"/*开时钟 打开外设对应的时钟(查看参考手册,该外设挂在哪个数据总线上),对应GPIO在哪条总线开哪条GPIOF外设 挂在AHB1总线上,所以要打开AHB1的时钟,双击函数,右键->go to definition*/void LED_Init(void)
{//第一步:使能GPIOF的时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟//第二步:GPIOF9,F10 初始化设置GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 对应 IO 口GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO//第三步:设置灯的初始状态GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 设置高电平,灯灭
}
my_usart.h
#ifndef __MY_USART1_H__
#define __MY_USART1_H__
#include <stdint.h>void USART1_Config(void); //串口配置
void USART1_IRQHandler(void);
#endif
my_usart.c
#include "stm32f4xx.h"
#include "myusart.h"
#include "myled.h"
#include "string.h"
#include <stdio.h> /******************************第一步:串口时钟使能,GPIO 时钟使能。第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。第三步:GPIO 初始化设置:要设置模式为复用功能。第四步:串口初始化:设置波特率,字长,奇偶校验等参数。第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。第六步:使能串口。第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。*********************************/void USART1_Config(void)
{//第一步:串口时钟使能,GPIO 时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);// 第三步:GPIO 初始化设置:要设置模式为复用功能。GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 115200;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_Mode_Rx;USART_Init(USART1, &USART_InitStructure);// 第五步:开启中断并且初始化 NVIC,使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 第六步:使能串口USART_Cmd(USART1, ENABLE);}// 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
// USART1中断服务程序
void USART1_IRQHandler(void)
{// 检查USART1是否接收到数据if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {// 读取接收到的字符char received_char = USART_ReceiveData(USART1);// 存储接收到的字符串的缓冲区static char buffer[10];static uint8_t index = 0;if (received_char != '\n' && received_char != '\r')//接收数据的结束标志{// 将字符存储到缓冲区buffer[index] = received_char;index++;} else{//将字符串末尾设置为'\0'buffer[index] = '\0';index = 0; //置0,为下一次存储做准备// 检查接收到的字符串是否为"open"if (strcmp(buffer, "open") == 0) {// 点亮LED灯GPIO_ResetBits(GPIOF, GPIO_Pin_9| GPIO_Pin_10); }}}
}//对printf()函数进行重定向
int fputc(int ch, FILE *f)
{USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕return ch;
}
注意:因为我们在程序上面设置了必须输入回车或者换行,串口才认可接收到的数据,所以必须在发送数据后再发送一个回车符, 这里 XCOM 提供的发送方法是通过勾选发送新行实现,如图,只要勾选了这个选项,每次发送数据后,XCOM 都会自动多发一个回车(0X0D+0X0A)。设置好了发送新行,我们再在发送区输入发送的命令:open,然后单击发送,这样灯便会点亮!
main.c代码:
#include <stdio.h>
#include "myusart.h"
#include "myled.h"int main(void)
{LED_Init();USART1_Config();while (1){// 主循环中无需处理接收数据,接收数据在中断中处理}
}
七、主从机通信的两种方式
至此,我们的本次的学习就结束了。通过以上几个实验,相信对串口通信有了深入的理解,这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!
相关文章:

嵌入式开发十八:USART串口通信实验
上一节我们学习了串口通信的基本理论,串口通信是学习单片机的一个重要的一步,非常重要,这一节我们通过实验来学习串口通信的使用,以及串口的接收中断的使用。 一、发送单个字节uint8_t数据或者字符型数据 实现的功能:…...

redis复习
redis知识点 redis持久化redis 订阅发布模式redis主从复制哨兵模式redis雪崩,穿透缓存击穿(请求太多,缓存过期)缓存雪崩 redis持久化 redis是内存数据库,持久化有两种方式,一种是RDB(redis dat…...

SUSE linux的快照和恢复
snapper用于创建和管理文件系统快照,并在需要时实现回滚,它还可以用于创建用户数据的磁盘备份。snapper使用btrfs文件系统或者精简配置的被格式化成XFS或EXT4的LVM卷。snapper可以通过命令行或YaST来进行管理。 btrfs是一种copy-on-write文件系统&#x…...
【Qt快速入门(六)】- QLineEdit按钮的使用
目录 Qt快速入门(六)- QLineEdit按钮的使用QLineEdit按钮的使用QLineEdit的基本用法1. 创建和设置文本2. 获取输入文本3. 清空输入文本 文本处理1. 选择文本2. 设置光标位置3. 撤销和重做 输入验证1. 输入掩码2. 校验器3. 输入限制 样式设置1. 设置字体和…...
常用损失函数详解:广泛使用的优化约束方法
各类常用损失函数详解:广泛使用的优化约束方法 今天介绍下损失函数,先介绍下我常用的方法SmoothedL1,它是一个平滑的L1 penalty函数,用于处理约束violation。 标准的L1 penalty函数定义为: L 1 ( x ) { 0 , if x ≤ 0 x , if x > 0 …...
鸿蒙开发组件:【创建DataAbility】
创建DataAbility 实现DataAbility中Insert、Query、Update、Delete接口的业务内容。保证能够满足数据库存储业务的基本需求。BatchInsert与ExecuteBatch接口已经在系统中实现遍历逻辑,依赖Insert、Query、Update、Delete接口逻辑,来实现数据的批量处理。…...

配电室数据中心巡检3d可视化搭建的详细步骤
要搭建配电室巡检的3D可视化系统,可以按照以下步骤进行: 收集配电室数据: 首先,需要收集配电室的相关数据,包括配电室的布局、设备信息、传感器数据等。可以通过实地调查、测量和设备手册等方式获取数据。 创建3D模型…...

TIME_WAIT的危害
前言 该文章主要讨论下TIME_WAIT的存在意义和潜在危害,以及解决措施。 具体内容 首先看一下下面这幅图 这幅图来自《TCP IP详解卷1:协议 原书第2版中文》TCP状态变迁图。 TIME_WAIT存在意义 可靠的终止TCP连接。 保证让迟来的TCP报文有足够的时间被…...

搜维尔科技邀您共赴2024第四届轨道车辆工业设计国际研讨会
会议内容 聚焦“创新、设计、突破”,围绕“面向生命健康、可持续发展的轨道交通系统” 为主题,从数字化、智能化、人性化、绿色发展等方面,探索轨道交通行业的设计新趋势及发展新机遇。 举办时间 2024年7月10日-12日 举办地点 星光岛-青岛融…...
智能中人类造成的风险、机器造成的风险、环境造成的风险
在使用智能技术时,可能会面临各种类型的风险。以下是一些可能的风险情况: 1、人类造成的风险 错误判断和决策:人类在使用智能系统时可能会因为各种原因做出错误的判断和决策,导致不良后果。人为错误:技术操作人员、维护…...
MYSQL基础查询
示例:user_profile iddevice_idgenderageuniversityprovince12138male21北京大学Beijing23214male复旦大学Shanghai36543female20北京大学Beijing42315female23浙江大学Zhejiang55432male25山东大学Shandong 查询所有列 select * from user_profile;查询…...
【Golang】Go 中的生产者-消费者模式
Go 中的生产者-消费者模式 来源:https://medium.com/@mm.nikfarjam/the-producer-consumer-pattern-in-go-cf97299a0320 文章目录 Go 中的生产者-消费者模式介绍关键组件在 Go 中的实现结论Go 中的生产者-消费者模式 介绍 生产者-消费者模式是处理大数据的最常见设计模式之一…...

【通过新能源汽车的智慧数字底盘技术看计算机的相关技术堆栈?以后是软硬结合的全能程序员的天下,取代全栈(前后端都会的全栈程序员)】
汽车的“智慧数字底盘”是一个综合性的技术平台,旨在提升车辆的性能、安全性和驾驶体验。它集成了多种先进的技术和系统,是全能程序员的必杀技! 1. 传感器技术 a. 激光雷达(LiDAR) 用于生成高分辨率的3D地图&#…...

Python网络爬虫4-实战爬取pdf
1.需求背景 爬取松产品中心网站下的家电说明书。这里以冰箱为例:松下电器-冰箱网址 网站分析: 第一步: 点击一个具体的冰箱型号,点击了解更多,会打开此型号电器的详情页面。 第二步:在新打开的详情页面中…...

超神级!Markdown最详细教程,程序员的福音
超神级!Markdown最详细教程,程序员的福音Markdown最详细教程,关于Markdown的语法和使用就先讲到这里,如果喜欢,请关注“IT技术馆”。馆长会更新最实用的技术!https://mp.weixin.qq.com/s/fNzhLFyYRd3skG-…...

Android OTA 升级基础知识详解+源码分析
前言: 本文仅仅对OTA升级的几种方式的概念和运用进行总结,仅在使用层面对其解释。需要更详细的内容我推荐大神做的全网最详细的讲解: https://blog.csdn.net/guyongqiangx/article/details/129019303?spm1001.2014.3001.5502 三种升级方式…...

【吊打面试官系列-Mysql面试题】SQL 语言包括哪几部分?每部分都有哪些操作关键字?
大家好,我是锋哥。今天分享关于 【SQL 语言包括哪几部分?每部分都有哪些操作关键字?】面试题,希望对大家有帮助; SQL 语言包括哪几部分?每部分都有哪些操作关键字? SQL 语言包括数据定义(DDL)、…...

Redis的缓存击穿与解决
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。 Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 解决方案 互斥锁 实现 逻辑过期 实现 解决方案…...

网络层 IP协议【计算机网络】【协议格式 || 分片 || 网段划分 || 子网掩码】
博客主页:花果山~程序猿-CSDN博客 文章分栏:Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长! 目录 一,前提 二&…...

Python学习笔记14:进阶篇(三)。类的终结篇,类的导入和模块的导入。
前言 这篇文章属于类知识的最后一篇,带一点点其他知识,学习内容来自于Python crash course。 关注我私信发送Python crash course,分享一份中文版PDF。 类的导入 在学习的时候,包括之前,我都是在一个文件中把所有代…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...