嵌入式开发十八: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时,表示发送数据寄存器已空,可以发送下一个字节
  • 具体来说,发送数据过程包括:
    1. 将数据写入TDR。
    2. 数据从TDR移到移位寄存器。
    3. 当移位寄存器开始发送数据时,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

  1. tmp_h = 0x120x1234右移8位得到高8位0x12
  2. tmp_l = 0x340x12340xff按位与得到低8位0x34
  3. 调用Usart_SendByte(pUSARTx, 0x12)发送高8位。
  4. 调用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)
{
    // 配置USART1
    USART1_Config();

    while (1)
    {
        // 主循环中无需处理接收数据,接收数据在中断中处理
    }
}

六、向单片机发送指令点亮LED

实现的功能:

    STM32F4 通过串口和上位机通信,发送字符串(指令): open(通过串口助手),然后单片机接收,点亮LED灯;

本实验主要是串口通信的控制功能,演示串口通信可以增强硬件的能力!

my_led..h

#ifndef __MYLED_H
#define __MYLED_H

void 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;//100MHz
   GPIO_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)
    {
        // 主循环中无需处理接收数据,接收数据在中断中处理
    }
}

七、主从机通信的两种方式

至此,我们的本次的学习就结束了。通过以上几个实验,相信对串口通信有了深入的理解,这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/724539.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

conda下安装32位版本python

前言&#xff1a;当前主流的系统为64bit系统&#xff0c;conda软件为64bit软件&#xff0c;因此使用conda创建虚拟环境安装python时默认安装的python为64bit版本&#xff0c;但部分研发场景需要调用32bit依赖&#xff0c;只能使用32bit的python&#xff0c;因此需要安装32bit的…

KVB投资安全小知识:如何识别一个货币是避险货币还是风险货币?

摘要 在全球经济不断变化的今天&#xff0c;理解货币的避险属性和风险特征对投资者至关重要。本文将详细探讨如何准确识别一个货币是避险货币还是风险货币&#xff0c;并结合具体的货币案例分析它们的本质差异。通过深入分析不同因素对货币走势的影响&#xff0c;帮助读者在金…

【面试八股总结】Redis数据结构及底层实现

一、五种基本数据结构 Redis 提供了丰富的数据类型&#xff0c;常见的有五种数据类型&#xff1a;String&#xff08;字符串&#xff09;&#xff0c;Hash&#xff08;哈希&#xff09;&#xff0c;List&#xff08;列表&#xff09;&#xff0c;Set&#xff08;集合&#xff0…

【Java】已解决java.util.EmptyStackException异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.util.EmptyStackException异常 一、问题背景 java.util.EmptyStackException是Java在使用java.util.Stack类时可能会遇到的一个异常。这个异常通常在尝试从空的栈中弹出&am…

[吃瓜教程]概览西瓜书+南瓜书第1、2章

第一章 绪论 1.1机器学习的定义,什么是机器学习&#xff1f; 1&#xff09;机器学习是这样一门学科&#xff0c;它致力于研究如何通过计算的手段&#xff0c;利用经验来改善系统自身的性能。 2&#xff09;机器学习所研究的主要内容是关于在计算机上从数据中产生模型的算法&a…

【golang学习之旅】Go程序快速开始 Go程序开发的基本注意事项

系列文章 【golang学习之旅】使用VScode安装配置Go开发环境 【golang学习之旅】报错&#xff1a;a declared but not used 【golang学习之旅】Go 的基本数据类型 【golang学习之旅】深入理解字符串string数据类型 【golang学习之旅】go mod tidy 【golang学习之旅】记录一次 p…

linux配置Vnc Server给Windows连接

1. linux 安装必要vnc server和桌面组件 sudo apt -y install tightvncserversudo apt install xfce4 xfce4-goodies2. linux 配置vncserver密码 #bash vncserver参考: https://cn.linux-console.net/?p21846#google_vignette 3. 将启动桌面命令写入.vnc/xstartup # .vnc/x…

了解指标体系1:指标是大数据开发中的关键要素

在大数据开发的过程中&#xff0c;指标体系是一个至关重要的概念。本文将介绍什么是指标&#xff0c;为什么它们如此重要&#xff0c;以及如何在大数据项目中有效地构建和应用指标体系。 目录 什么是指标&#xff1f;指标的类型为什么指标如此重要&#xff1f;如何构建有效的指…

2024最新自动化测试 —— Jest 测试框架应用

什么是自动化测试 在软件测试中&#xff0c;自动化测试指的是使用独立于待测软件的其他软件来自动执行测试、比较实际结果与预期并生成测试报告这一过程。在测试流程已经确定后&#xff0c;测试自动化可以自动执行的一些重复但必要的测试工作。也可以完成手动测试几乎不可能完…

xcode和iPhone真机或者watch真机连接问题

1.如果真机是第一次连接xocde&#xff0c;就需要开启真机上的开发者模式&#xff0c;开启开发者模式的方式&#xff1a; iphone/ipad开启方式: 设置 > 隐私与安全 > 开发者模式 > 开启&#xff0c;然后重启就可以了 watch设置&#xff1a;很麻烦&#xff0c;看文章…

对身外之物,不必在意

不管对待自己还是他人&#xff0c;外在的条件不值得挂怀&#xff0c;人在不得已颠沛流离时期&#xff0c;吃不好、穿不好&#xff0c;都应该从容接受。

外汇天眼:盈透证券为客户提供更丰富的欧洲衍生品交易渠道

电子交易巨头盈透证券&#xff08;纳斯达克代码&#xff1a;IBKR&#xff09;今日宣布&#xff0c;通过Cboe欧洲期权交易所&#xff08;CEDX&#xff09;新增欧洲股票期权和欧洲指数期货及期权。这一新增功能使得盈透证券的客户可以在单一统一平台上&#xff0c;除了股票、期权…

力扣469A

文章目录 1. 题目链接2. 题目代码3. 题目总结4. 代码分析 1. 题目链接 I Wanna Be the Guy 2. 题目代码 #include<iostream> #include<set> using namespace std; int main(){int highestLevelOfGame;cin >> highestLevelOfGame;set<int> levelCanPas…

【profinet】从站开发要点

目录 0、常见缩写及关键字注释 1、profinet简介 2、profinet协议栈 3、profinet数据帧 4、profinet网络解决方案示例 5、Application areas 注&#xff1a;本文主要简述profinet从站开发涉及到的知识点。【不足之处后续慢慢补充】。 0、常见缩写及关键字注释 MRP: Media…

移动硬盘接入mac无法复制文件进去怎么办,mac里的文件如何存进移动硬盘

如果要传输的文件数据量比较大&#xff0c;相比于使用U盘&#xff0c;移动硬盘是更多的选择。移动硬盘可存储量大、传输速度快&#xff0c;是实用性比较强的储存设备。不仅是Windows设备&#xff0c;Mac电脑也经常使用到移动硬盘。但有时候&#xff0c;移动硬盘在Mac上不能传文…

XHS-Downloader是一款小红书图片视频下载工具

这款软件可以提取账号发布、收藏、点赞作品链接&#xff1b;提取搜索结果作品链接、用户链接&#xff1b;下载小红书作品信息&#xff1b;提取小红书作品下载地址&#xff1b;下载小红书无水印作品文件&#xff01; &#x1f4d1; 功能清单 ✅ 采集小红书图文 / 视频作品信息…

【Pmac】PMAC QT联合开发中各种可能遇到的坑

目录 1. 错误 C2027 使用了未定义类型“PCOMMSERVERLib::DEVUPLOAD”2. 输入了正确的pmac的ip地址&#xff0c;没有显示可选的pmac设备3. Pmac DTC-28B无读数 使用QT编写PMAC上位机程序时&#xff0c;利用QT中的dump工具可以将pcommserver.exe转化为pcommserverlib.h和pcommser…

AMBA-CHI协议详解(四)

《AMBA 5 CHI Architecture Specification》 AMBA-CHI协议详解&#xff08;一&#xff09; AMBA-CHI协议详解&#xff08;二&#xff09; AMBA-CHI协议详解&#xff08;三&#xff09; AMBA-CHI协议详解&#xff08;四&#xff09; 文章目录 2.3.3 Atomic transactions2.3.4 S…

Hadoop3:MapReduce中的Shuffle机制

一、流程图 Shuffle是Map方法之后&#xff0c;Reduce方法之前的数据处理过程称。 二、图解说明 1、数据流向 map方法中context.write(outK, outV);开始&#xff0c;写入环形缓冲区&#xff0c;再进行分区排序&#xff0c;写到磁盘 reduce方法拉取磁盘上的数据&#xff0c;…

swift使用swift-protobuf协议通讯,使用指北

什么是Protobuf Protobuf&#xff08;Protocol Buffers&#xff09;协议&#x1f609; Protobuf 是一种由 Google 开发的二进制序列化格式和相关的技术&#xff0c;它用于高效地序列化和反序列化结构化数据&#xff0c;通常用于网络通信、数据存储等场景。 为什么要使用Proto…