• USART串口协议


    通信接口

    通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
    通信协议:制定通信的规则,通信双方按照协议规则进行数据收发

     

     全双工:指通信双方能够同时进行双向通信,一般来说,全双工的通信都有两根通信线,发送线路和接收线路互不影响;

    半双工:数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输;

    单工:指数据只能从一个设备到另一个设备,不可以反着来;

    时钟的作用:如果输出了一段高电平的波形,就是依靠时钟来判断波形是两个高电平信号还是一个高电平信号,即告诉接收方什么时候需要采集数据,I2C和SPI都有单独的时钟线,接收方可以在时钟信号的指引下进行采样,其他的没有时钟线,所以需要双方约定一个采样频率,为异步通信;

    电平:单端:引脚的高低电平都是对GND的电压差,所以单端信号通信的双方都必须要共地,即把GND接在一起;差分:靠两个差分引脚的电压差来传输信号的,在通信时可以不需要GND,差分信号可以极大地提高抗干扰特性,所以差分信号的传输速度和距离都会非常高。

    设备特性:多设备:可以在总线上挂载多个设备,还需要寻址的操作 ;点对点:两个设备直接传输数据;

    串口

    串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
    单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力

     

     硬件电路

    简单双向串口通信有两根通信线(发送端 TX 和接收端 RX
    TX RX 要交叉连接
    当只需单向的数据传输时,可以只接一根通信线
    当电平标准不一致时,需要加电平转换芯片

     

     电平标准

    电平标准是数据 1 和数据 0 的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
    TTL 电平: +3.3V +5V 表示 1 0V 表示 0
    RS232 电平: -3~-15V 表示 1 +3~+15V 表示 0
    RS485 电平:两线压差 +2~+6V 表示 1 -2~-6V 表示 0 (差分信号)

     串口参数及时序

    波特率:串口通信的速率(决定了每隔几秒发送一位数据)
    起始位:标志一个数据帧的开始,固定为低电平(产生下降沿,表示开始)
    数据位:数据帧的有效载荷, 1 为高电平, 0 为低电平,低位先行
    (即先发送数据的低位,由低位到高位输出)
    校验位:用于数据验证,根据数据位计算得来(奇校验和偶校验,效果不如CRC校验)
    停止位:用于数据帧间隔,固定为高电平(产生上升沿,表示结束,方便下一次的下降沿开始)

     

     USART简介

    USART Universal Synchronous/Asynchronous Receiver/Transmitter )通用同步 / 异步收发器
    USART STM32 内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从 TX 引脚发送出去,也可自动接收 RX 引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
    自带波特率发生器,最高达 4.5Mbits/s
    可配置数据位长度( 8/9 )、停止位长度( 0.5/1/1.5/2
    可选校验位(无校验 / 奇校验 / 偶校验)
    支持同步模式、硬件流控制、 DMA 、智能卡、 IrDA LIN
    STM32F103C8T6 USART 资源: USART1 USART2 USART3

    USART框图

    引脚处只需要注意TX(发送)和RX(接收)引脚即可

     发送数据寄存器和接收数据寄存器共用一个地址DR的,TDR是只写的,RDR是只读的,当我们想要写入DR时,写入的是TDR,读出DR时,读出的是RDR。

    发送移位寄存器工作方式:当我们给TDR写入一个数据时,此时硬件检测到写入数据了,就会检查当前移位寄存器是否有数据正在移位,如果没有,那么我们输入的数据就会立刻全部移动到发送移位寄存器中,准备发送,在数据从TDR移动到移位寄存器时,会置一个标志位TXE,发送寄存器空,我们就可以检查这个标志位,如果置1了,我们就可以给TDR写入下一个数据了。然后发送移位寄存器就会在发生器控制的驱动下,向右一位一位地把数据输出到TX引脚(低位先行),当移位完成后,新的数据就会再次自动地从TDR转移到发送移位寄存器中来;如果移位寄存器中的数据还未移位完成,那么TDR就会进行等待,一旦移位完成就会立马把数据转移过来。(双重缓存)

     接收移位寄存器同理。置的标志位位RXNE

    SCLK:作用1可以兼容别的协议,作用2可以做自适应波特率

    硬件数据流控制:用得少,不解释

    唤醒单元:可以用来实现多设备通信的功能

    波特率发生器部分:实际上就是分频器,USART1挂载在APB2,所以就是PCLK2的时钟,一般是72M,其他的USART都挂载在APB1,所以是PCLK1的时钟,一般是36M,之后这个时钟进行一个分频,除一个USARTDIV的分频系数,之后分频完之后再除个16,的带发送器时钟和接收器时钟,通向控制部分。如果TE为1,则发送部分的波特率有效,RE为1,则接收部分的波特率有效。

    USART基本结构

    数据帧

    字长设置

    停止位 

    起始位侦测(排除噪声)(P26-25:36)

    数据采样

    波特率发生器

    发送器和接收器的波特率由波特率寄存器 BRR 里的 DIV 确定
    计算公式:波特率 = f PCLK2/1 / (16 * DIV)

     

     DIV分为整数部分和小数部分,可以实现更细腻的分频

    代码实操

    串口发送

    根据引脚定义,我们计划使用USART1的TX和RX引脚,所以要接到PA9和PA10上,注意接收脚和发送脚要交替相接;

    介绍相关函数

    老朋友

    1. void USART_DeInit(USART_TypeDef* USARTx);
    2. void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
    3. void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
    4. void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
    5. void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

     配置同步时钟输出(时钟是否需要输出,时钟的极性相位等)

    1. void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
    2. void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);

     开启USART到DMA的触发通道

    void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);
    

     发送数据和接收数据(即写DR寄存器和读DR寄存器)

    1. void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    2. uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

     四个标志位函数(完成和中断)

    1. FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    2. void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    3. ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
    4. void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

    按照下图流程编写初始化函数

    1、开启时钟,把需要用到的USART和GPIO的时钟打开

    1. //开启时钟
    2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    2、GPIO初始化,把TX配置成复用输出,RT配置成输入

    1. //GPIO
    2. GPIO_InitTypeDef GPIO_InitStructure;
    3. //TX是USART外设控制的输出脚,使用复用推挽输出
    4. //RX是USART外设数据输入表,使用输入模式
    5. //因为串口波形空闲状态时高电平,所以一般使用浮空输入或者上拉输入
    6. //此代码只需要发送
    7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    10. GPIO_Init(GPIOA, &GPIO_InitStructure);

    3、配置USART

    1. //USART
    2. USART_InitTypeDef USART_InitStructure;
    3. //波特率(直接写我们需要的波特率即可,函数可以帮我们算好相对应的分频系数
    4. USART_InitStructure.USART_BaudRate = 9600;
    5. //硬件流控制(只使用RTX或者CTX、或者不用、或者都用)
    6. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    7. //USART模式(TX或者RX,当两者都想用时就可以使用|符号,类似GPIO选择两个引脚)
    8. USART_InitStructure.USART_Mode = USART_Mode_Tx;
    9. //校验(Odd奇校验,Even偶校验,No无)
    10. USART_InitStructure.USART_Parity = USART_Parity_No;
    11. //停止位
    12. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    13. //字长
    14. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    15. USART_Init(USART1, &USART_InitStructure);

    4、开启开关(如果需要接收信息,还需要配置中,这样就需要再加上ITConfig和NVIC的代码)

    1. USART_Cmd(USART1, ENABLE);

    5、创建一个发送数据的函数

    1. void Serial_SendByte(uint8_t Byte)
    2. {
    3. USART_SendData(USART1, Byte);
    4. //判断TDR的数据是否转移到移位寄存器
    5. //第二个参数:传输数据寄存器TDR空标志
    6. //在数据手册25.6.1中对TXE描述为再次对DR进行写操作时,即再次调用SendData函数时
    7. //标志位会自动置0,所以我们不用手动清零了
    8. while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    9. }

    再在主函数中调用试一下

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. Serial_SendByte(0x41);
    10. while(1)
    11. {
    12. }
    13. }

    得到

    数据模式 

    HEX 模式 / 十六进制模式 / 二进制模式:以原始数据的形式显示
    文本模式 / 字符模式:以原始数据编码后的形式显示

     当然我们仅有一个串口输出一个字节的数据是远远不够的,我们还可以封装其他用处的函数,例如发送一个数组,发送一个字符串,发送一个数字

    发送一个数组

    1. void Serial_SendArray(uint16_t *Array, uint8_t Size)
    2. {
    3. uint8_t i;
    4. for (i = 0; i < Size; i ++)
    5. {
    6. Serial_SendByte(Array[i]);
    7. }
    8. }

     这里调用了之前所定义的发送字节函数

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. uint16_t Array1[] = {0x41, 0x42, 0x43, 0x44};
    10. Serial_SendArray(Array1, 4);
    11. while(1)
    12. {
    13. }
    14. }

     发送字符串

    1. void Serial_SendString(char *String)
    2. {
    3. uint8_t i;
    4. //每个字符串都是以'\0'结尾的
    5. //且字符串的长度总比其看起来多一个字节'\0'
    6. for (i = 0; String[i] != '\0'; i ++)
    7. {
    8. Serial_SendByte(String[i]);
    9. }
    10. }
    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. Serial_SendString("Hello World!\r\n");
    10. while(1)
    11. {
    12. }
    13. }

    发送数字(无符号)

    没想象中这么简单

    1. uint32_t Serial_PowNum(uint16_t Num, uint8_t m)
    2. {
    3. uint32_t Result = 1;
    4. while(m--)
    5. {
    6. Result *= Num;
    7. }
    8. return Result;
    9. }
    10. void Serial_SendNum(uint32_t Num, uint8_t Length)
    11. {
    12. uint8_t i;
    13. for (i = 0; i < Length; i ++)
    14. {
    15. //这里是以字符的形式输出的
    16. //所以要加上'0'的一个偏移
    17. Serial_SendByte(Num / Serial_PowNum(10, Length - i - 1) % 10 + '0');
    18. }
    19. }
    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. Serial_SendNum(1234, 4);
    10. while(1)
    11. {
    12. }
    13. }

    这些函数的原理与OLED中的相关代码类似 

    移植c中的printf函数

    首先需要先打开MicroLIB(keil为嵌入式平台优化的一个精简库)

      如此写(在串口的.c文件中)

    1. #include
    2. //重定向printf
    3. int fputc(int ch, FILE *f)
    4. {
    5. Serial_SendByte(ch);
    6. return ch;
    7. }

    为什么重定向fputc就可以移植printf函数为我们所用呢?这是因为printf函数是以fputc为底层,调用printf打印的过程就是不断的调用fputc这个函数来实现的,只要重定向了fputc就可以修改printf函数,与我们封装串口打印数组字符串类似,是以Serial_SendByte为底层的。

    我们只需再在头文件中声明即可

    1. #ifndef __SERIAL_H__
    2. #define __SERIAL_H__
    3. #include
    4. void Serial_Init(void);
    5. void Serial_SendByte(uint8_t Byte);
    6. void Serial_SendArray(uint16_t *Array, uint8_t Size);
    7. void Serial_SendString(char *String);
    8. void Serial_SendNum(uint32_t Num, uint8_t Length);
    9. #endif

    在主函数中调用

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. printf("Num=%d\r\n", 666);
    10. while(1)
    11. {
    12. }
    13. }

    但是这样移植的话只能在串口1中使用,串口2就没法使用了,如何移植可以让多个串口使用移植的printf函数呢?

    改善的方法1

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. char String[100];
    10. //sprintf可以指定打印位置
    11. //这里是通过把内容打印到String中,然后再通过串口字符串输出来输出内容
    12. sprintf(String, "Num=%d\r\n", 666);
    13. Serial_SendString(String);
    14. while(1)
    15. {
    16. }
    17. }

    改善的方法2

    因为方法1需要每次定义一个字符串,且这个字符串参数是不可变的

    我们可以通过定义可变参数或者封装函数来改善移植printf函数的方法

    知识盲区.....

    1. #include
    2. void Serial_Printf(char *format, ...)
    3. {
    4. char String[100];
    5. va_list arg;
    6. va_start(arg, format);
    7. vsprintf(String, format, arg);
    8. va_end(arg);
    9. Serial_SendString(String);
    10. }
    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. Serial_Printf("\r\nNum4=%d", 444);
    10. Serial_Printf("\r\n");
    11. while (1)
    12. {
    13. }
    14. }

    显示汉字的方法

    UTF8

    然后串口助手选择文本编码为UTF8即可实现

    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. int main(void)
    6. {
    7. OLED_Init();
    8. Serial_Init();
    9. Serial_Printf("你好,世界");
    10. while(1)
    11. {
    12. }
    13. }

    总体

    1. #include "stm32f10x.h" // Device header
    2. #include
    3. #include
    4. void Serial_Init(void)
    5. {
    6. //开启时钟
    7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    9. //GPIO
    10. GPIO_InitTypeDef GPIO_InitStructure;
    11. //TX是USART外设控制的输出脚,使用复用推挽输出
    12. //RX是USART外设数据输入表,使用输入模式
    13. //因为串口波形空闲状态时高电平,所以一般使用浮空输入或者上拉输入
    14. //此代码只需要发送
    15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    16. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    17. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    18. GPIO_Init(GPIOA, &GPIO_InitStructure);
    19. //USART
    20. USART_InitTypeDef USART_InitStructure;
    21. //波特率(直接写我们需要的波特率即可,函数可以帮我们算好相对应的分频系数
    22. USART_InitStructure.USART_BaudRate = 9600;
    23. //硬件流控制(只使用RTX或者CTX、或者不用、或者都用)
    24. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    25. //USART模式(TX或者RX,当两者都想用时就可以使用|符号,类似GPIO选择两个引脚)
    26. USART_InitStructure.USART_Mode = USART_Mode_Tx;
    27. //校验(Odd奇校验,Even偶校验,No无)
    28. USART_InitStructure.USART_Parity = USART_Parity_No;
    29. //停止位
    30. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    31. //字长
    32. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    33. USART_Init(USART1, &USART_InitStructure);
    34. USART_Cmd(USART1, ENABLE);
    35. }
    36. //发送一个字节的数据
    37. void Serial_SendByte(uint8_t Byte)
    38. {
    39. USART_SendData(USART1, Byte);
    40. //判断TDR的数据是否转移到移位寄存器
    41. //第二个参数:传输数据寄存器TDR空标志
    42. //在数据手册25.6.1中对TXE描述为再次对DR进行写操作时,即再次调用SendData函数时
    43. //标志位会自动置0,所以我们不用手动清零了
    44. while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    45. }
    46. /**
    47. * @brief 发送一个数组
    48. * @param Array:数组名字;Size:数组长度
    49. * @retval 无
    50. */
    51. void Serial_SendArray(uint16_t *Array, uint8_t Size)
    52. {
    53. uint8_t i;
    54. for (i = 0; i < Size; i ++)
    55. {
    56. Serial_SendByte(Array[i]);
    57. }
    58. }
    59. /**
    60. * @brief 发送一个字符串
    61. * @param String:字符串
    62. * @retval 无
    63. */
    64. void Serial_SendString(char *String)
    65. {
    66. uint8_t i;
    67. //每个字符串都是以'\0'结尾的
    68. //且字符串的长度总比其看起来多一个字节'\0'
    69. for (i = 0; String[i] != '\0'; i ++)
    70. {
    71. Serial_SendByte(String[i]);
    72. }
    73. }
    74. /**
    75. * @brief 辅助发送数字
    76. * @param Num:底数;m:幂
    77. * @retval Num的m次方
    78. */
    79. uint32_t Serial_PowNum(uint16_t Num, uint8_t m)
    80. {
    81. uint32_t Result = 1;
    82. while(m--)
    83. {
    84. Result *= Num;
    85. }
    86. return Result;
    87. }
    88. /**
    89. * @brief 发送数字
    90. * @param Num:数字; Length:数字的位数
    91. * @retval 无
    92. */
    93. void Serial_SendNum(uint32_t Num, uint8_t Length)
    94. {
    95. uint8_t i;
    96. for (i = 0; i < Length; i ++)
    97. {
    98. //这里是以字符的形式输出的
    99. //所以要加上'0'的一个偏移
    100. Serial_SendByte(Num / Serial_PowNum(10, Length - i - 1) % 10 + '0');
    101. }
    102. }
    103. //重定向printf
    104. int fputc(int ch, FILE *f)
    105. {
    106. Serial_SendByte(ch);
    107. return ch;
    108. }
    109. void Serial_Printf(char *format, ...)
    110. {
    111. char String[100];
    112. va_list arg;
    113. va_start(arg, format);
    114. vsprintf(String, format, arg);
    115. va_end(arg);
    116. Serial_SendString(String);
    117. }

    串口输入和输出

    在定义GPIO口时把Pin_10加上(上拉输入)

    1. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    2. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    3. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    4. GPIO_Init(GPIOA, &GPIO_InitStructure);

    同时开启输入模式和输出模式

    1. //USART模式(TX或者RX,当两者都想用时就可以使用|符号,类似GPIO选择两个引脚)
    2. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

    串口接收数据的方法有两种:

    1、查询:即循环查询接收数据的标志位RXNE,当查询到标志位时,通过OLED显示出来;

    实际工程不推荐使用,因为CPU一直在查询这件事上等着,不能进一步进行其他工作,所以不推荐使用。

    2、中断:通过判断标志位RXNE的变化,当查询到标志位时,立即进入中断,通过OLED显示出来,不影响接下来程序的执行。

    中断

    配置NVIC

    1. //开启RXNE的标志位中断
    2. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    3. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    4. NVIC_InitTypeDef NVIC_InitStructure;
    5. //中断通道的选择、使能还是失能
    6. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    7. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    8. //优先级(无所谓)
    9. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    10. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    11. NVIC_Init(&NVIC_InitStructure);

    思路:先定义两个变量便于主函数获取串口接收到的数据

    其一是存储接收值,其二是标志着接收值已经赋予了前者

    通过中断判断RXNE标志位的变化,把串口值赋予RXData,然后再把RXFlag置1,提醒主函数已经把接收置赋予到定义的变量上了(其实可以直接对RXData加一个extern定义在.h文件中,然后就可以在主函数中直接访问这个变量的值即可,这样写可以不用自己写一个标志位,文章中写的方法是为了下一节做铺垫,因为这一节的代码只能接收一个字节的数据)

    1. uint8_t Serial_RXFlag;//标志串口接收值已经赋予了RXData
    2. uint16_t Serial_RXData;//存储串口接收值
    3. //用于获取自建的标志位
    4. uint8_t Serial_GetRXFlag(void)
    5. {
    6. if (Serial_RXFlag == 1)
    7. {
    8. //检测标志位位1后立马清零
    9. //以便下次获取串口接收值
    10. Serial_RXFlag = 0;
    11. return 1;
    12. }
    13. return 0;
    14. }
    15. //获取串口接收到的值
    16. uint16_t Serial_GEtRXData(void)
    17. {
    18. return Serial_RXData;
    19. }
    20. //中断函数
    21. void USART1_IRQHandler(void)
    22. {
    23. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
    24. {
    25. //把接收到的值存储到变量中
    26. Serial_RXData = USART_ReceiveData(USART1);
    27. //每当串口接收到值,就把标志位置1,提醒主函数串口接收到了值
    28. Serial_RXFlag = 1;
    29. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    30. }
    31. }
    1. #include "stm32f10x.h" // Device header
    2. #include "Delay.h"
    3. #include "OLED.h"
    4. #include "Serial.h"
    5. uint16_t Data;
    6. int main(void)
    7. {
    8. OLED_Init();
    9. Serial_Init();
    10. OLED_ShowString(1, 1, "SerialData:");
    11. while(1)
    12. {
    13. if(Serial_GetRXFlag()==1)
    14. {
    15. Data = Serial_GEtRXData();
    16. //在串口的输入区显示
    17. Serial_SendByte(Data);
    18. OLED_ShowHexNum(1, 12, Data, 2);
    19. }
    20. }
    21. }

     

  • 相关阅读:
    项目经理的证书,都有哪些?
    css基本样式之字体样式
    装机必备的浏览器推荐,干净好用,选这4款不会出错
    05-在Idea中编写Servlet程序
    软件测试人员的职业发展之路,写给那些还在迷茫的测试人
    MySQL慢查询优化、索引优化和表优化总结
    2、ARM处理器概论
    【程序的编译和预处理】
    ThreadLocal笔记
    什么是 SRE?一文详解 SRE 运维体系
  • 原文地址:https://blog.csdn.net/m0_74460550/article/details/133420251