STM32使用串口发送接收数据
前言
串口通信一般用作异步通信,使用两根数据线进行收发:TX 和 RX,为利用USART 实现开发板与电脑通信,需要用到一个USB 转USART 的 IC,我们选择 CH340G 芯片来实现这个功能。本次实验采用串口1即usart1进行试验,将CH340G的TX连接单片机PA10,RX连接PA9,两块芯片共地,并且采用3.3V供电。
接下来就是代码实现部分:
1.打开外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
2.对串口的端口进行配置
//串口1对应引脚复用映射 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
对于这个函数复用映射理解如下:
stm32f4的所有外设没有默认引脚,当使用外设时必须要使用GPIO_PinAFConfig函数选择引脚进行端口复用。也可以理解为stm32f4没有重映射,就是选择对应的端口进行端口复用。
相比之下stm32f1的外部中断与其他外设的端口复用都要用AFIO(即端口重映射)
如果不使用重映射,端口复用直接使用第一端口的话(外部中断除外),不需要使用AFIO。
对于f1来说查阅数据手册,复用直接使用第一端口,并且串口接收中断是内部中断,所以不需要使用AFIO
再对PA9和PA10进行初始化
//USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
本来PA10要设置为浮空输入模式,但是对于stm32f4来说
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;这种模式已经不存在,在F4的库里边只有这个模式:GPIO_Mode_AF。所有使用复用功能的引脚都应设置成这种模式。这样比F1方便多了。你不用考虑这个引脚应该设置成浮空输入还是推挽输出,因为引脚设置错误基本可以消失。
在STM32F1里边,这样方式配置时钟复用
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1,ENABLE);
打开复用时,但是在F4里边这个就不管用了。库里边根本就没有了这个变量RCC_APB2Periph_AFIO
总结原因:F4没有了时钟复用功能。配置完成之后,需要用GPIO_PinAFConfig()设置功能映射。非常方便
3.对串口外设进行初始化
//USART1 初始化设置 USART_InitStructure.USART_BaudRate = bound;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 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_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1
对于串口发送来说,此时已经配置完成。串口数据接收有两种方式:查询和中断,查询使用函数,主函数不断检测是否接收标志位,不方便
if(USART_GetFlagStatus(USART1,USART_FLAG_RXE)== SET);
串口数据中断接收需要进行额外的配置,每接收一个数据会产生相应的内部中断,所以需要配置NVIC
代码如下:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
4.串口数据发送
/***********************************************************/ //发送一个字节 void Sendbyte(uint8_t Byte) { USART_SendData(USART1, Byte); //调用发送数据函数 while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送完成 } //调用 Sendbyte('A');//发送一个字节 /***********************************************************/ /***********************************************************/ //发送一个数组 void SendArray(uint8_t *Array,uint16_t Length) //数组无法判断是否结束,所以再传递Length { uint16_t i; for(i=0; i<Length ; i++) { Sendbyte(Array[i]); } } //发送数组,也就是连续发送多个字节 uint8_t MyArray[] = {0x40,0x41,0x42,0x43,0x44}; SendArray(MyArray,5); /***********************************************************/ /***********************************************************/ //发送字符串 void SendString(char *String)//字符串自带结束标志位,无需再传递参数 { uint16_t i; for(i=0; String[i] != '\0'; i++) //'\0'也可直接写0,结束标志位 { Sendbyte(String[i]); } } //调用 SendString("HelloWorld\r\n"); /***********************************************************/ /***********************************************************/ //使用打印函数 #include "stdio.h" //加入以下代码,支持printf函数,而不需要选择use MicroLIB #if 1 #pragma import(__use_no_semihosting) //标准库需要的支持函数 struct __FILE { int handle; }; FILE __stdout; //定义_sys_exit()以避免使用半主机模式 void _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 //主函数调用,这种方法只能在一个串口使用,比如此时用的串口1 printf("Num = %d\r\n",666); //别的串口要使用打印,可以选sprintf,该函数可以指定打印位置 char String[100]; sprintf(String,"Num = %d\r\n",666); SendString(String); /***********************************************************/
5.串口数据接收
前面已经配置好了相关中断,接下来只需要在中断服务函数里面写代码
void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收中断,每接收一个数据产生一个中断 { Res =USART_ReceiveData(USART1);//(USART1->DR);//读取接收到的数据 Sendbyte(Res); //将接收到的数据再发出去 } }
本文作者:京多安
本文链接:https://www.cnblogs.com/manchestercity/p/17863250.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步