本篇博文目录:
一.通信相关概念1.通信的三种分类(1) 串行通信和并行通信(2) 同步传输和异步传输(3) 半双工,全双工,单工
2.常见的通信方式及其分类
二.串口通信相关概念1.串口通信2.串口通信的几个比较重要的参数3.串口的硬件接线(1) DB9和DB25(2) 交叉线和直通线(3) USB转串口(4) RS232和RS485
4.STM32串口工作过程分析(1) 波特率控制(2) 收发控制(3) 数据存储转移部分
三.STM32串口通信发送,接收和重定向代码(标准库版本)四.源码
一.通信相关概念
1.通信的三种分类
通信按数据传输方式进行分类,可以分为串行通信和并行通信;按通信机制方式进行分类,可以分为同步传输和异步传输。按照数据传输方向和能力进行分类,可以分为单工,半双工和全双工。
(1) 串行通信和并行通信
串行通信:数据是按照单个比特一个接一个地传输的,需要在数据位之间插入控制位,以确保数据的传输正确。串行通信的优点是使用较少的导线,在长距离传输时更稳定,但传输速度相对较慢。
![在这里插入图片描述](https://img-blog.csdnimg.cn/000b8c479f684225b71557c745b149d0.png)
并行通信:数据是同时传输多个比特,需要更多的导线来实现。并行通信的优点是传输速度快,但成本高且容易产生电源噪声等干扰。
![在这里插入图片描述](https://img-blog.csdnimg.cn/36fafcf2da4c4ec4b219e0c8ac75e775.png)
备注:图片来源于https://www.eefocus.com/e/526547.html
(2) 同步传输和异步传输
同步通信:发送方和接收方使用共同的时钟信号进行数据传输和控制,这种方式的数据传输速度较快,但需要在发送和接收端维护相同的时钟,并且相对复杂。
![在这里插入图片描述](https://img-blog.csdnimg.cn/dd78e0c31e52465faad92e1fe35992ec.png)
异步通信:在数据传输过程中不需要任何时钟同步信号,而是在数据之间通过特定的协议进行数据同步。这使得异步通信更加简单易用,但传输速率相对较慢,数据传输可靠性也较差。
![在这里插入图片描述](https://img-blog.csdnimg.cn/90b6e52cfa944993bed7501290f8f902.png)
(3) 半双工,全双工,单工
单工(Simplex):单工通信只能在一个方向上进行信息传输,即信息只能从一个方向的发送器传送到另一个方向的接收器,无法进行双向传输。例如,电视广播和无线电广播就是一种单向通信,只能通过广播信号发送信息,而无法接收信息反馈。
![在这里插入图片描述](https://img-blog.csdnimg.cn/4ac9a9510d5c462cb21f82c56dbd063a.png)
半双工(Half Duplex):半双工通信只能在同一时间内进行单向传输,即信息只能从一个方向的发送器传送到另一个方向的接收器,不能同时进行双向传输。例如,对讲机就是一个典型的半双工通信设备,同一时间内只能有一方进行发言,另一方只能听取,不能同时说话和听取。
![在这里插入图片描述](https://img-blog.csdnimg.cn/4bf3f872e0fd4eba8109a20e8a11f37c.png)
全双工(Full Duplex):全双工通信可以在同一时间内进行双向传输,即信息可以同时从两个方向进行传输,例如电话通信、网络通信等。在全双工通信中,每个通信实体都拥有一个发送器和一个接收器,能够在不影响对方通信的情况下进行同时的双向数据传输。
![在这里插入图片描述](https://img-blog.csdnimg.cn/8dd5602511bc4513a314ad07f7c704b6.png)
备注:图片来源于https://zhuanlan.zhihu.com/p/361655746
2.常见的通信方式及其分类
同步/异步+串行/并行
类型描述通信协议数据传输速率传输距离应用同步串行通信发送方与接收方在时钟方面保持同步,逐位地按照固定的时间间隔同时发送或接收数据SPI、I2C、Microwire等Mbps通常不超过一个设备的长度短距离通信、芯片与芯片之间的通信异步串行通信发送方和接收方不需要保持时钟同步,每个数据字节之间有一个起始位和一个或多个停止位,实现了帧同步UART、RS-232等Kbps通常不超过几十米数据采集、远程控制、较短距离通信同步并行通信发送方和接收方在时钟上保持同步,同时传输多个数据位ISA总线Mbps通常不超过一个设备的长度高速数据传输异步并行通信发送方和接收方不需要时钟同步,在特定情况下可以同时传送多个数据位USB、PCI总线等Gbps通常不超过几米高速数据传输、外部存储设备
其他通信:
类型描述通信协议数据传输速率传输距离应用USB通信通过USB接口连接各种外部设备,支持异步传输、同步传输和流传输等多种工作模式USB 1.x/2.x/3.xGbps通常不超过几米外部设备连接、高速数据传输CAN通信用于工业自动化和汽车电子领域的数据总线标准,支持多个节点之间的数据传输CAN 2.0BMbps数百米至数千米工业自动化、汽车电子Ethernet通信基于帧格式的网络协议,用于互联网和局域网通信,支持快速、可靠的数据传输IEEE 802.3Gbps通常不超过几百米网络通信、数据中心、局域网WiFi通信基于无线局域网技术的通信方式,通过无线网络实现设备之间的数据传输IEEE 802.11Gbps通常不超过几十米移动设备、家庭网络、公共场所Bluetooth通信短距离、低功耗的无线通信技术,主要用于移动设备之间的数据传输Bluetooth 4.0/5.0Mbps通常不超过几十米移动设备、家庭娱乐、智能家居
二.串口通信相关概念
1.串口通信
① 串口通信是指通过串行通信接口来进行数据传输的一种通信方式。串口通信有两种基础协议:同步协议和异步协议,其中异步协议广泛应用于单片机开发中。 ② 在异步协议中,通信双方的时钟并不需要进行同步,而是利用起始位和停止位来判断每一个字节的开始和结束。异步通信中最常用的协议是UART(Universal Asynchronous Receiver-Transmitter),UART只需要使用一根发送线和一根接收线即可完成单向或双向的串口通信。 ③ 串口通信可以实现点对点和多点通信,实现简单,价格低廉,但数据传输速率较慢,一般采用的通信速率为115200bps以下。在单片机开发中,串口通信被广泛应用于与电脑之间的数据传输、与传感器之间的数据传输以及与其他单片机之间的数据传输等场景。(注意有时终线的是同步,没有时钟线的是异步,同步串行通信数据帧如下图所示 )
![在这里插入图片描述](https://img-blog.csdnimg.cn/466ac3f9a3e7463cb3d053733991970b.png)
例如:MCU单片机给PC机发送一个字符 ‘A’,的通信过程大致如下:
首先是硬件连接,只需要三根线分别是RXD,TXD和GND;需要注意的是MCU的RXD和PC的TXD进行连接,MCU的TXD和PC的RXD进行连接(这里的引脚是真实引脚,交叉线)。
![在这里插入图片描述](https://img-blog.csdnimg.cn/a8cf76aa0b6d4460b4188ef1baf83aa3.png)
MCU进行数据发送,只需要MCU的TXD和PC机的RXD进行工作,发送一个字符’A’的波形如下图所示,首先是起始位通常都是低电平,然后后面1~8(0 ~ 7)位为数据位,其中第八位(0 ~ 7 中的第7位)可能为奇偶校验位,看用户是否进行设置,在0 ~ 7 位中只有0和6是高电平,这里高电平就是我们发送的有效数据,将高电平对应位的二进制转换成十进制2^0 + 2^6 = 1+64 = 65 会发现这里的65正好对应ASCLL字符’A’, 接着最后一位为停止位(通常为高电平)表示本次数据发送结束。
![在这里插入图片描述](https://img-blog.csdnimg.cn/bb23e12ba0d048bc81260a74d5ffdb21.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/07ec01d3d0f1428f8fc90aa9a72a455c.png)
在首次进行串口通信的时候,我们常常会发现会出现乱码或数据接收不到的情况,这通常原因就是MCU和PC二者之间的串口参数配置不一致导致的,比如波特率,这里的波特率指的就是发送数据的速度,比如波特率9600表示1s钟发送9600个这样的数据。
![在这里插入图片描述](https://img-blog.csdnimg.cn/8cc22e93acb248dcbfa5ee67b54bc80e.png)
上面内容如果还不明白可以通过这个视频进行学习https://www.bilibili.com/video/BV1y34y147s5/,进一步了解串口通信
![在这里插入图片描述](https://img-blog.csdnimg.cn/abf45f781318400eb670a489750ec887.png)
2.串口通信的几个比较重要的参数
串口通信的几个比较重要的参数包括:
波特率(Baud Rate):表示通信双方之间每秒传输的比特数,通常用bps(bits per second)来表示。波特率越高,数据传输速度越快,但同时也会增加误码率,降低传输可靠性。 数据位(Data Bits):表示每个字节中所包含的数据位数,常见的取值有5、6、7和8位。数据位的选择取决于数据所包含的信息量以及传输的精度要求。 停止位(Stop Bits):表示每个字节传输结束后,发送方在发送线上保持的电平状态,通常有1位和2位两种取值。停止位的作用是为了告诉接收方当前数据传输已经结束。 校验位(Parity):在数据传输过程中,加入一个校验位来判断数据是否正确传输。常见的校验方式有奇偶校验、偶校验、无校验等,其中奇偶校验最为常用。
以上参数需要双方进行协商确定,并在通信时保持一致,否则数据传输将会出现错误。在单片机开发中,常常使用的串口通信参数为波特率9600bps、8数据位、1停止位、无校验位。
3.串口的硬件接线
(1) DB9和DB25
串口线的接口标准通常有DB9和DB25两种,其中DB9有9根引脚,适用于较少信号的串口设备,而DB25有25根引脚,适用于需要传输大量信号的串口设备。在串口线的选择时,需要根据具体的设备类型和接口标准进行选择。同时,还需要注意串口线的长度和屏蔽效果,以确保数据传输的稳定性和可靠性。
DB9
DB9是一种常用的串口接口标准,也称作DE-9(D形9针),其中“D”指代连接器的外形类似于字母D。DB9接口共有9个引脚,分别为:
DCD(Data Carrier Detect) - 数据载波检测RD(Data Receive) - 数据接收TD(Data Transmit) - 数据发送DTR(Data Terminal Ready) - 数据终端就绪GND(Signal Ground) - 路径接地DSR(Data Set Ready) - 数据集就绪RTS(Request To Send) - 请求发送CTS(Clear To Send) - 清除发送RI(Ring Indicator) - 响铃指示
DB9接口广泛应用于串口设备之间的通信,如串口打印机、调制解调器、路由器、终端设备等。在使用DB9接口时,需要注意接口针脚的正确连接和通信参数的正确设置,以确保数据传输的稳定和可靠。
![在这里插入图片描述](https://img-blog.csdnimg.cn/804797549f5c43ac8d746d23c8633c0c.png)
DB25
DB25是一种串口接口标准,也称作DE-25(D形25针),其中“D”指代连接器的外形类似于字母D。DB25接口共有25个引脚,与DB9接口相比,其拥有更多的信号引脚,能够支持更复杂的通信方式。DB25接口的引脚定义如下:
1.protective ground(保护接地) 2.TxD(Data Transmit) - 数据发送 3.RxD(Data Receive) - 数据接收 4.RTS(Request To Send) - 请求发送 5.CTS(Clear To Send) - 清除发送 6.DSR(Data Set Ready) - 数据集就绪 7.Signal ground(Signal Ground) - 信号地 8.CD(Carrier Detect) - 载波检测 9.RI(Ring Indicator) - 响铃指示 10.交替行选通 11.设备选择1 12.设备选择2 13.设备选择3 14.设备选择4 15.线路就绪 16.时钟 17.端口就绪 18.暂停 19.申请握手 20.结束握手 21.数据线就绪1 22.数据线就绪2 23.数据线就绪3 24.数据线就绪4 25.保留
DB25接口广泛应用于串口设备之间的通信,如计算机、打印机、调制解调器、路由器、终端设备等。在使用DB25接口时,需要注意接口针脚的正确连接和通信参数的正确设置,以确保数据传输的稳定和可靠。
![在这里插入图片描述](https://img-blog.csdnimg.cn/f0eba6e33392495cbffac91f818b9d24.png)
(2) 交叉线和直通线
串口的连接需要结合实际原理图来连接,看是采用的交叉线方式还是直通线方式,如果采用交叉线方式就把单片机的TXD与其他设备的RXD相连,RXD与TXD相连;如果采用的是直通线就把单片机的TXD与其他设备的TXD相连,RXD与RXD相连。
交叉线
标准串口引脚2是RX,引脚3是TX,(即九针串口的2号引脚连接的是主控芯片的RXD引脚,3号引脚连接的是主控芯片的TXD引脚),如果单片机开发板的串口和电脑串口都是标准串口,则俩串口应该用交叉线连接。(单片机的TXD连接电脑的RXD;单片机的RXD连接串口的RXD)
直通线
不过现实中直通线居多,为了配合直通线的使用,在画板的时候,把主控芯片的RXD引脚连接至串口的3脚(RXD实际是TXD),主控芯片的TXD引脚连接至串口的2脚(TXD实际是RXD),这样这个开发板上的串口就不是标准串口了,即2变成TX,3变成RX,和标准串口连接时当然应该使用直连线了。(单片机的TXD(实际是RXD)和电脑的TXD相连;单片机的RXD(实际是TXD)和电脑RXD相连)
备注:参考了这篇博文:https://new.qq.com/rain/a/20210527A01UNY00
(3) USB转串口
① USB转串口是指通过USB接口将计算机与串口设备进行连接的一种方式。由于现代笔记本电脑大多已经取消了串口接口,而很多应用场合仍需要使用串口设备进行通信,因此USB转串口逐渐成为了一种常见的解决方案。 ② USB转串口一般需要使用专门的转换器,这种转换器内部会集成一个串口芯片和USB芯片,可以实现USB和串口之间的信号转换和协议转换。在使用时,用户只需要将USB转串口转换器插入计算机的USB接口上,就可以通过串口通信软件来访问串口设备进行数据传输。 ③ 需要注意的是,不同的USB转串口转换器支持的串口标准可能不同,如RS232、TTL等,因此在选择转换器时需要确认其支持的串口标准是否符合自己的需求。同时,在使用USB转串口转换器时也需要特别注意其驱动程序是否已经安装,以及通信参数的设置是否正确。
![在这里插入图片描述](https://img-blog.csdnimg.cn/83e932cde9034e95b964ad08400b9fbf.png)
(4) RS232和RS485
串口通信采用的电平为TTL电平,这种电平输出的高电平最低为2.4V,低电平最高为0.4V,如果在传输的过程中受到干扰(静电),可能将某一个低电平变高,所以串口通信通常的距离很短,一般为1m之内。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2420aa9364184702843e97ed1f6b5cdf.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/c95c915f0e464ffa8937a1302f0d3497.png)
所以串口通信常用于MCU与MCU或MCP和PC之间进行通信
![在这里插入图片描述](https://img-blog.csdnimg.cn/f401c47f83754470b37b1ac804864028.png)
为了提供通信的稳定性有了RS232和RS485通信,所谓的RS232通信指的是将TTL电平转换为RS232电平,这里的转换常用负逻辑,意思就是TTL的+5V转换为-12V,0V转换为12V这样就是-12V表示高电平,+12V表示低电平(二者之间可以分别进行转换),并且RS232的电平范围要比TTL电平大一些,从而提高通信的稳定。
![在这里插入图片描述](https://img-blog.csdnimg.cn/385ab4e8543e4529a3f54d4ec57dfcd6.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2e7ed7d484564de7a3ae42f751e2cb7c.png)
RS232的通信距离可以达到15m,但是速率仅有20k,也就是19200的波特率
![在这里插入图片描述](https://img-blog.csdnimg.cn/037149a082dd4c94a33133db6f4645b9.png)
针对一些工业环境或者对距离有更远的要求,RS232就力不从心了,所以这时就可以考虑采用RS485通信;RS485通信就是在MCU外加一个485转换芯片,RS485采用差分信号,传递的高低电平通过RS485_A和RS485_B线来传递,当RS485_A>RS485_B时为高电平,当RS485_A
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
/***************** 发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i=0; i
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
/***************** 发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
/* 取出高八位 */
temp_h = (ch&0XFF00)>>8;
/* 取出低八位 */
temp_l = ch&0XFF;
/* 发送高八位 */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低八位 */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
// 初始化中断优先级
static void NVIC_Configuration(void){
NVIC_InitTypeDef NVIC_InitStructure;// 中断控制器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 配置优先分组
// 中断源
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
// 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
// 子优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
// 使能中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// 初始化NVIC
NVIC_Init(&NVIC_InitStructure);
}
// 初始化串口
void USART1_Config(){
// 初始化GPIO_InitTypeDef和USART_InitTypeDef结构体对象
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 开启USART1和GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
// 【1】配置引脚
// 初始化GPIO9(PA9作为TXD)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;// 复用推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;// 初始化的引脚PA9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 设置GPIO的速率为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIO
// 初始化GPIO10(PA10作为RXD)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//初始化的引脚PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;// 浮空输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 设置GPIO的速率为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIO
// 【2】串口中断优先级
NVIC_Configuration();
// 【3】初始化USART1
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 ;// 奇偶校验位0
// 无硬件流模式:在无硬件流控制模式下,数据通信没有任何限制,也就是说,当接收缓冲区溢出时会发生数据丢失
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 指定硬件流模式为无硬件流模式
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 工作模式:同时启用串口的发送和接收模式
USART_Init(USART1, &USART_InitStructure);// 初始化串口
// 【4】 使能串口和接收中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);// 接收数据不为空的中断
USART_Cmd(USART1, ENABLE);// 串口使能
USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除接收中断标志位
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
// 防止下标越界
if(USART1_RX_Index >=USART1_RX_MAX){
memset(USART1_RX_Buffer,0,USART1_RX_MAX);// 将缓存清0
USART1_RX_Index = 0;// 下标复位
}else{
ucTemp = USART_ReceiveData(USART1);// 从缓冲区读取一个字节的数据
USART1_RX_Buffer[USART1_RX_Index++] = (char)ucTemp; // 将数据放入到缓存区中
if(USART1_RX_Buffer[USART1_RX_Index - 1] == '\n'){// 表示最后一条数据接收完毕
// 解析指令
if(strstr(USART1_RX_Buffer, "led:0") != NULL)
{
led_run = 0;
ledOnOrOff(LED_OFF);
printf("灯已关");
}else if(strstr(USART1_RX_Buffer, "led:1") != NULL)
{
led_run = 1;
ledOnOrOff(LED_ON);
printf("灯已开");
}else{
printf("未知指令");
}
// 清除缓冲区的数据
memset(USART1_RX_Buffer,0,USART1_RX_MAX);// 将缓存清0
USART1_RX_Index = 0;// 下标复位
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除接收中断标志位
}
}
uart1.h
#ifndef __uart1_h
#define __uart1_h
#include "stm32f10x.h"
#include
#define USART1_RX_MAX 255
void USART1_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
#endif /*__uart1_h */
led.c
#include "led.h"
#include "stm32f10x.h"
// 初始化LED
void ledInit(){
// PB上的时钟使能
RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK,ENABLE);
// 初始化GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(LED_G_GPIO_PORT,&GPIO_InitStruct);// 初始化GPIO
}
// LED的开或关
void ledOnOrOff(int state){
// 打开LED
if(state == LED_ON){
// 置低电平
GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
}else if(state == LED_OFF){
// 置高电平
GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
}
}
// 状态反转
void ledToggle(){
// 通过odr寄存器
LED_G_GPIO_PORT->ODR ^=LED_G_GPIO_PIN;// ODR:0000 LED_G_GPIO_PIN:0x0400 (主要看第二位)进行异或后:0000与0100异或结果为0100;ODR=0100 再次与LED_G_GPIO_PIN(0100)异或,异或后0000;依次循环实现状态反转
}
-led.h
#ifndef __led_h
#define __led_h
#define LED_G_GPIO_PIN GPIO_Pin_10
#define LED_G_GPIO_PORT GPIOB
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_ON 1
#define LED_OFF 0
#define LED_G(state) if(state) GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN); else GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
void ledInit(void);
void ledOnOrOff(int state);
void ledToggle(void);
#endif /* __led_h */
main.c
#include "stm32f10x.h"
#include "uart1.h"
#include "string.h"
#include "led.h"
int main(void){
// 初始化LED
ledInit();
ledOnOrOff(LED_OFF);
// 初始化串口
USART1_Config();
while(1){
}
}
运行效果: ![在这里插入图片描述](https://img-blog.csdnimg.cn/a4b57325aae84a18b5de0f1df1d504c4.png)
四.源码
微信公众号,回复 串口源码 既可以获取本篇博文的源代码,如果有什么问题,后台留言我看见会第一时间回复你的喔。
![在这里插入图片描述](https://img-blog.csdnimg.cn/6b33937ab7714618ab569846dd0baf68.jpeg)
|