• 嵌入式开发十八:USART串口通信实验


           上一节我们学习了串口通信的基本理论,串口通信是学习单片机的一个重要的一步,非常重要,这一节我们通过实验来学习串口通信的使用,以及串口的接收中断的使用。下面是简单的串口时序图,清晰的展示了数据的发送过程

    一、发送单个字节uint8_t数据或者字符型数据

    实现的功能:

           STM32F4 通过串口和上位机通信,发送单个字节数据(0-255)或者字符给上位机,然后显示在电脑串口助手上。

    my_usart.h文件内容

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void My_UsartInit(void); //串口初始化
    5. void Usart_SendByte(uint8_t date); //发送一字节函数
    6. #endif

    my_usart.c文件内容

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include //对printf()函数进行重定向引入头文件
    4. /******************************
    5. 第一步:串口时钟使能,GPIO 时钟使能。
    6. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    7. 第三步:GPIO 初始化设置:要设置模式为复用功能
    8. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    9. 第五步:使能串口
    10. */
    11. void My_UsartInit(void)
    12. {
    13. //1.第一步:串口时钟使能,GPIO 时钟使能
    14. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    15. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    16. //2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    17. GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    18. GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
    19. //3.第三步:GPIO 初始化设置:要设置模式为复用功能
    20. GPIO_InitTypeDef Struct1;
    21. Struct1.GPIO_Mode=GPIO_Mode_AF;
    22. Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;
    23. Struct1.GPIO_Speed=GPIO_Speed_100MHz;
    24. GPIO_Init(GPIOA,&Struct1);
    25. //4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    26. USART_InitTypeDef Struct2;
    27. Struct2.USART_BaudRate=115200; //设置波特率
    28. Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位
    29. Struct2.USART_Parity=USART_Parity_No; //无校验位
    30. Struct2.USART_StopBits=USART_StopBits_1; //1位停止位
    31. Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控
    32. Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送
    33. USART_Init(USART1,&Struct2);
    34. //5.第五步:使能串口
    35. USART_Cmd(USART1,ENABLE);
    36. }
    37. //发送一字节(一个字符)函数
    38. void Usart_SendByte(uint8_t data)
    39. {
    40. USART_SendData(USART1,data); //这是一个库函数,用于将一个字节的数据写入USART的数据寄存器
    41. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    42. }
    43. //对printf()函数进行重定向
    44. int fputc(int ch, FILE *f)
    45. {
    46. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    47. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    48. return ch;
    49. }

     main.c代码

    1. #include
    2. #include "myusart.h"
    3. int main(void)
    4. {
    5. My_UsartInit();
    6. printf("发送一个字节:\n");
    7. Usart_SendByte('a');
    8. printf("\n");
    9. Usart_SendByte(97);
    10. printf("\n");
    11. while(1)
    12. {
    13. }
    14. }

    关键部分解读:

         发送过程:调用串口发送函数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文件内容

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void My_UsartInit(void); //串口初始化
    5. void USART_SendHalfWord(uint16_t data); //发送两个字节函数
    6. #endif

    my_usart.c

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include //对printf()函数进行重定向引入头文件
    4. /******************************
    5. 第一步:串口时钟使能,GPIO 时钟使能。
    6. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    7. 第三步:GPIO 初始化设置:要设置模式为复用功能
    8. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    9. 第五步:使能串口
    10. */
    11. void My_UsartInit(void)
    12. {
    13. //1.第一步:串口时钟使能,GPIO 时钟使能
    14. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    15. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    16. //2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    17. GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    18. GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
    19. //3.第三步:GPIO 初始化设置:要设置模式为复用功能
    20. GPIO_InitTypeDef Struct1;
    21. Struct1.GPIO_Mode=GPIO_Mode_AF;
    22. Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;
    23. Struct1.GPIO_Speed=GPIO_Speed_100MHz;
    24. GPIO_Init(GPIOA,&Struct1);
    25. //4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    26. USART_InitTypeDef Struct2;
    27. Struct2.USART_BaudRate=115200; //设置波特率
    28. Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位
    29. Struct2.USART_Parity=USART_Parity_No; //无校验位
    30. Struct2.USART_StopBits=USART_StopBits_1; //1位停止位
    31. Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控
    32. Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送
    33. USART_Init(USART1,&Struct2);
    34. //5.第五步:使能串口
    35. USART_Cmd(USART1,ENABLE);
    36. }
    37. //发送两个字节(16位)数据函数
    38. void USART_SendHalfWord(uint16_t data)
    39. {
    40. // 分离高8位和低8位
    41. uint8_t tmp_h = data >>0x08;//将date右移8位,取得高8位数据并赋值给tmp_h。右移8位相当于将高8位移到低8位的位置,高8位的原位置被0填充。
    42. uint8_t tmp_l = data & 0xFF;//将date和0xff(255,二进制为11111111)高八位为0,进行按位与运算,取得低8位数据并赋值给tmp_l。按位与运算将高8位清零,仅保留低8位。
    43. // 发送高8位
    44. USART_SendData(USART1, tmp_h);
    45. while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    46. // 发送低8位
    47. USART_SendData(USART1, tmp_l);
    48. while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    49. }
    50. //对printf()函数进行重定向
    51. int fputc(int ch, FILE *f)
    52. {
    53. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    54. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    55. return ch;
    56. }

    main.c

    1. #include
    2. #include "myusart.h"
    3. int main(void)
    4. {
    5. My_UsartInit();
    6. //发送两个字节的数据
    7. USART_SendHalfWord(0xffee);
    8. while(1)
    9. {
    10. }
    11. }

    关键部分解读:

           串行通信接口通常一次只能处理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

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void My_UsartInit(void); //串口初始化
    5. void Usart_SendByte(uint8_t date); //发送一字节函数
    6. void USART1_SendArray(uint8_t *array, uint16_t length);//发送一个数组
    7. #endif

    my_usart.c

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include //对printf()函数进行重定向引入头文件
    4. /******************************
    5. 第一步:串口时钟使能,GPIO 时钟使能。
    6. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    7. 第三步:GPIO 初始化设置:要设置模式为复用功能
    8. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    9. 第五步:使能串口
    10. */
    11. void My_UsartInit(void)
    12. {
    13. //1.第一步:串口时钟使能,GPIO 时钟使能
    14. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    15. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    16. //2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    17. GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    18. GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
    19. //3.第三步:GPIO 初始化设置:要设置模式为复用功能
    20. GPIO_InitTypeDef Struct1;
    21. Struct1.GPIO_Mode=GPIO_Mode_AF;
    22. Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;
    23. Struct1.GPIO_Speed=GPIO_Speed_100MHz;
    24. GPIO_Init(GPIOA,&Struct1);
    25. //4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    26. USART_InitTypeDef Struct2;
    27. Struct2.USART_BaudRate=115200; //设置波特率
    28. Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位
    29. Struct2.USART_Parity=USART_Parity_No; //无校验位
    30. Struct2.USART_StopBits=USART_StopBits_1; //1位停止位
    31. Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控
    32. Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送
    33. USART_Init(USART1,&Struct2);
    34. //5.第五步:使能串口
    35. USART_Cmd(USART1,ENABLE);
    36. }
    37. //发送一字节函数
    38. void Usart_SendByte(uint8_t data)
    39. {
    40. USART_SendData(USART1,data);
    41. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    42. }
    43. //发送一个数组
    44. void USART1_SendArray(uint8_t *array, uint16_t len)
    45. {
    46. for (uint16_t i = 0; i < len; i++)
    47. {
    48. Usart_SendByte(array[i]);
    49. }
    50. }
    51. //对printf()函数进行重定向
    52. int fputc(int ch, FILE *f)
    53. {
    54. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    55. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    56. return ch;
    57. }

    main.c

    1. #include
    2. #include "myusart.h"
    3. int main(void)
    4. {
    5. My_UsartInit();
    6. // 要发送的字节数组
    7. uint8_t dataArray1[] = {1,2,3,4,5,6,7,8,9,10};
    8. int len =sizeof(dataArray1) / sizeof(dataArray1[0]);
    9. // 发送一个数组
    10. USART1_SendArray(dataArray1, len);
    11. while (1)
    12. {
    13. }
    14. }

    四、发送字符串数据

    实现的功能:

           STM32F4 通过串口和上位机通信,发送字符串给电脑,然后显示在电脑串口助手上。

    my_usart.h文件内容

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void My_UsartInit(void); //串口初始化
    5. void USART1_SendString(char *str); //发送一个字符串
    6. #endif

    my_usart.c文件内容

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include //对printf()函数进行重定向引入头文件
    4. /******************************
    5. 第一步:串口时钟使能,GPIO 时钟使能。
    6. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    7. 第三步:GPIO 初始化设置:要设置模式为复用功能
    8. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    9. 第五步:使能串口
    10. */
    11. void My_UsartInit(void)
    12. {
    13. //1.第一步:串口时钟使能,GPIO 时钟使能
    14. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    15. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    16. //2.第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数
    17. GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    18. GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
    19. //3.第三步:GPIO 初始化设置:要设置模式为复用功能
    20. GPIO_InitTypeDef Struct1;
    21. Struct1.GPIO_Mode=GPIO_Mode_AF;
    22. Struct1.GPIO_Pin=GPIO_Pin_9 |GPIO_Pin_10;
    23. Struct1.GPIO_Speed=GPIO_Speed_100MHz;
    24. GPIO_Init(GPIOA,&Struct1);
    25. //4.第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    26. USART_InitTypeDef Struct2;
    27. Struct2.USART_BaudRate=115200; //设置波特率
    28. Struct2.USART_WordLength=USART_WordLength_8b; //数据位8位
    29. Struct2.USART_Parity=USART_Parity_No; //无校验位
    30. Struct2.USART_StopBits=USART_StopBits_1; //1位停止位
    31. Struct2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不需要硬件流控
    32. Struct2.USART_Mode= USART_Mode_Rx |USART_Mode_Tx; //设置usart1既可以接收也可以发送
    33. USART_Init(USART1,&Struct2);
    34. //5.第五步:使能串口
    35. USART_Cmd(USART1,ENABLE);
    36. }
    37. //发送一个字符串
    38. void USART1_SendString(char *str)
    39. {
    40. while (*str!='\0')
    41. {
    42. USART_SendData(USART1,*str);
    43. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    44. str++;
    45. }
    46. }
    47. //对printf()函数进行重定向
    48. int fputc(int ch, FILE *f)
    49. {
    50. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    51. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    52. return ch;
    53. }

    main.c内容:

    1. #include
    2. #include "myusart.h"
    3. int main(void)
    4. {
    5. My_UsartInit();
    6. // 要发送的字符串
    7. char *dataString = "Hello World!";
    8. // 发送字符串
    9. printf("发送一个字符串:\n");
    10. USART1_SendString(dataString);
    11. printf("\n");
    12. while (1)
    13. {
    14. }
    15. }

    五、单片机接收电脑发送的数据显示在串口助手上(接收中断)

           使用串口接收中断可以让CPU在没有数据到达时执行其他任务,而不需要浪费时间轮询接收寄存器。这提高了CPU利用率和系统效率,检查标志位判断是否发生接收中断(USART_IT_RXNE),如果是,读取接收到的数据,然后通过清除接收中断标志以准备接收下一个字节。

    实现的功能:

          将接收数据设置为接收中断当上位机发送数据到STM32F407的USART1时,STM32会接收这个数据并通过中断处理函数将数据发送回上位机。上位机的串口助手会显示发送和接收的数据。

    my_usart.h

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void USART1_Config(void); //串口配置
    5. void USART1_SendByte(uint8_t data); //发送一个字节函数
    6. void USART1_IRQHandler(void); //中断服务函数
    7. #endif

    my_usart.c

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include //对printf()函数进行重定向引入头文件
    4. /******************************
    5. 第一步:串口时钟使能,GPIO 时钟使能。
    6. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
    7. 第三步:GPIO 初始化设置:要设置模式为复用功能。
    8. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    9. 第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。
    10. 第六步:使能串口。
    11. 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。
    12. */
    13. void USART1_Config(void)
    14. {
    15. //第一步:串口时钟使能,GPIO 时钟使能
    16. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    17. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    18. //第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
    19. GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    20. GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
    21. // 第三步:GPIO 初始化设置:要设置模式为复用功能。
    22. GPIO_InitTypeDef GPIO_InitStructure;
    23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
    24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    25. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    26. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    27. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    28. GPIO_Init(GPIOA, &GPIO_InitStructure);
    29. // 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    30. USART_InitTypeDef USART_InitStructure;
    31. USART_InitStructure.USART_BaudRate = 115200;
    32. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    33. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    34. USART_InitStructure.USART_Parity = USART_Parity_No;
    35. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    36. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    37. USART_Init(USART1, &USART_InitStructure);
    38. // 第五步:开启中断并且初始化 NVIC,使能USART1接收中断
    39. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    40. NVIC_InitTypeDef NVIC_InitStructure;
    41. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    42. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    43. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    44. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    45. NVIC_Init(&NVIC_InitStructure);
    46. // 第六步:使能串口
    47. USART_Cmd(USART1, ENABLE);
    48. }
    49. //发送一字节函数
    50. void USART1_SendByte(uint8_t data)
    51. {
    52. USART_SendData(USART1,data);
    53. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    54. }
    55. // 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
    56. void USART1_IRQHandler(void)
    57. {
    58. if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    59. {
    60. // 读取接收到的数据
    61. uint8_t receivedData = USART_ReceiveData(USART1);
    62. // 将接收到的数据发送回电脑
    63. USART1_SendByte(receivedData);
    64. // 清除中断标志
    65. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    66. }
    67. }
    68. //对printf()函数进行重定向
    69. int fputc(int ch, FILE *f)
    70. {
    71. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    72. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    73. return ch;
    74. }

    main.c

    1. #include "myusart.h"
    2. int main(void)
    3. {
    4. // 配置USART1
    5. USART1_Config();
    6. while (1)
    7. {
    8. // 主循环中无需处理接收数据,接收数据在中断中处理
    9. }
    10. }

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

    实现的功能:

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

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

    my_led..h

    1. #ifndef __MYLED_H
    2. #define __MYLED_H
    3. void LED_Init(void);
    4. #endif

    my_led.c

    1. #include "stm32f4xx.h" // Device header
    2. #include "myled.h"
    3. /*开时钟 打开外设对应的时钟(查看参考手册,该外设挂在哪个数据总线上),对应GPIO在哪条总线开哪条
    4. GPIOF外设 挂在AHB1总线上,所以要打开AHB1的时钟,双击函数,右键->go to definition*/
    5. void LED_Init(void)
    6. {
    7. //第一步:使能GPIOF的时钟
    8. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟
    9. //第二步:GPIOF9,F10 初始化设置
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0 和 LED1 对应 IO 口
    12. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    13. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    15. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    16. GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO
    17. //第三步:设置灯的初始状态
    18. GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10 设置高电平,灯灭
    19. }

    my_usart.h

    1. #ifndef __MY_USART1_H__
    2. #define __MY_USART1_H__
    3. #include
    4. void USART1_Config(void); //串口配置
    5. void USART1_IRQHandler(void);
    6. #endif

    my_usart.c

    1. #include "stm32f4xx.h"
    2. #include "myusart.h"
    3. #include "myled.h"
    4. #include "string.h"
    5. #include
    6. /******************************
    7. 第一步:串口时钟使能,GPIO 时钟使能。
    8. 第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
    9. 第三步:GPIO 初始化设置:要设置模式为复用功能。
    10. 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    11. 第五步:开启中断并且初始化 NVIC,使能中断(如果需要开启中断才需要这个步骤)。
    12. 第六步:使能串口。
    13. 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)。
    14. *********************************/
    15. void USART1_Config(void)
    16. {
    17. //第一步:串口时钟使能,GPIO 时钟使能
    18. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    19. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    20. //第二步:设置引脚复用器映射:调用 GPIO_PinAFConfig 函数。
    21. GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    22. GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
    23. // 第三步:GPIO 初始化设置:要设置模式为复用功能。
    24. GPIO_InitTypeDef GPIO_InitStructure;
    25. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
    26. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    27. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    28. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    29. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    30. GPIO_Init(GPIOA, &GPIO_InitStructure);
    31. // 第四步:串口初始化:设置波特率,字长,奇偶校验等参数。
    32. USART_InitTypeDef USART_InitStructure;
    33. USART_InitStructure.USART_BaudRate = 115200;
    34. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    35. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    36. USART_InitStructure.USART_Parity = USART_Parity_No;
    37. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    38. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    39. USART_Init(USART1, &USART_InitStructure);
    40. // 第五步:开启中断并且初始化 NVIC,使能USART1接收中断
    41. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    42. NVIC_InitTypeDef NVIC_InitStructure;
    43. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    44. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    45. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    46. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    47. NVIC_Init(&NVIC_InitStructure);
    48. // 第六步:使能串口
    49. USART_Cmd(USART1, ENABLE);
    50. }
    51. // 第七步:编写中断处理函数:函数名格式为 USARTxIRQHandler(x 对应串口号)
    52. // USART1中断服务程序
    53. void USART1_IRQHandler(void)
    54. {
    55. // 检查USART1是否接收到数据
    56. if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    57. {
    58. // 读取接收到的字符
    59. char received_char = USART_ReceiveData(USART1);
    60. // 存储接收到的字符串的缓冲区
    61. static char buffer[10];
    62. static uint8_t index = 0;
    63. if (received_char != '\n' && received_char != '\r')//接收数据的结束标志
    64. {
    65. // 将字符存储到缓冲区
    66. buffer[index] = received_char;
    67. index++;
    68. }
    69. else
    70. {
    71. //将字符串末尾设置为'\0'
    72. buffer[index] = '\0';
    73. index = 0; //置0,为下一次存储做准备
    74. // 检查接收到的字符串是否为"open"
    75. if (strcmp(buffer, "open") == 0)
    76. {
    77. // 点亮LED灯
    78. GPIO_ResetBits(GPIOF, GPIO_Pin_9| GPIO_Pin_10);
    79. }
    80. }
    81. }
    82. }
    83. //对printf()函数进行重定向
    84. int fputc(int ch, FILE *f)
    85. {
    86. USART_SendData(USART1,ch); //通过串口发送数据,每次发送一个字符
    87. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //等待发送完毕
    88. return ch;
    89. }

          注意:因为我们在程序上面设置了必须输入回车或者换行,串口才认可接收到的数据,所以必须在发送数据后再发送一个回车符, 这里 XCOM 提供的发送方法是通过勾选发送新行实现,如图,只要勾选了这个选项,每次发送数据后,XCOM 都会自动多发一个回车(0X0D+0X0A)。设置好了发送新行,我们再在发送区输入发送的命令:open,然后单击发送,这样灯便会点亮!

     

    main.c代码: 

    1. #include
    2. #include "myusart.h"
    3. #include "myled.h"
    4. int main(void)
    5. {
    6. LED_Init();
    7. USART1_Config();
    8. while (1)
    9. {
    10. // 主循环中无需处理接收数据,接收数据在中断中处理
    11. }
    12. }

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

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

  • 相关阅读:
    java实现List对象转geojson文本返回前端
    MySQL的事务
    linux系统Jenkins工具配置webhook自动部署
    java.util.Optional
    OpenCloudOS 开源操作系统社区成立;Azure 漏洞暴露数百个源代码存储库;AWS 遭遇本月第三次中断| 开源日报
    java中将List数据平均切分成N份
    使用Nginx来实现限流
    ES6快速入门(三)--拓展运算符
    Rstudio使用mlogti.data函数出现错误Error in guess(varying) : 没有能够从它们的名字中来猜测时变参数,如何解决?
    【Python】对MySQL数据库的操作(SQLAlchemy)
  • 原文地址:https://blog.csdn.net/weixin_47040031/article/details/139753183