完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
USART通用同步异步收发器(Universal Synchronous Asynchronous Receiver Transmitter)是一串行通信设备,可以灵活地与外部设备进行进行**全双工信息交换**
UART(Universal Asynchronous Receiver Transmitter),它是在 USART 的基础上裁剪了同步通信功能,只保留异步通信功能我们平时使用的串口通信都是 UART 1.通信接口背景知识介绍 1.1通信双方的两种通信方式
单工:数据只支持在一个方向上传输(看电视) **半双工:**同一时刻只允许在一个方向传输(对讲机) 全双工:允许同时在两个方向上传输(电话) 1.3常见的串行通信接口 2.USART功能概述 任何 UART 双向通信至少需要 3 个引脚,数据发送引脚 TXD,数据接收引脚 RXD,数 据参考地 GND。串口连接必须共地!!! 这里解释下电平标准,根据使用使用的电平标准不同,可以分为 TTL 和 RS232 标准,因为控制器一般都是 TTL 标准,因此如果需要进行 RS232 通信时,一定要使用 R232转换器进行 TTL 和 RS232 的电平转换 3.STM32串口资源
5.STM32串口通信使用方法 串口通信需要定义的参数
7.1对应库函数
下面是串口 1 的初始化函数,主要完成串口的设置。 /**/ /******************************************************************************* * 函数名 :串口初始化函数 * 函数功能 :初始化USRAT1的硬件设备 * 形参 : 无 * 返回值 : 无 *******************************************************************************/ /**/ void bsp_InitUart1(uint32_t baud) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; #if EN_USART1_RX //如果使能了接收 NVIC_InitTypeDef NVIC_InitStructure; #endif /*TX=PA9 RX=PA10*/ /*配置GPIO*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE); //打开GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //打开串口时钟 /*配置PA9与PA10两个端口的复用功能*/ //TX发送 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //PA9 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,&GPIO_InitStructure); //RX接收 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //PA10 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&GPIO_InitStructure); /*配置串口硬件参数*/ //波特率 USART_InitStructure.USART_BaudRate=baud; //形参传入 //数据位字长 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 ; //收发模式 //初始化串口1 USART_Init(USART1,&USART_InitStructure); /*如果使能接收*/ #if EN_USART1_RX /*配置串口中断*/ //通道 NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //响应优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority=3; //通道使能 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);//跟据指定参数初始化VIC寄存器 //开启串口接收中断 USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口空闲中断 USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); /* 其实发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。 空闲的定义是总线上在一个字节的时间内没有再接收到数据,空闲中 断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收 到数据的时候发生的。 */ #endif //使能串口 USART_Cmd(USART1,ENABLE); } 主要完成串口波特率,数据长度,校验位,流控,模式的设置,在这个函数里没有对GPIO 引脚做宏定义,是因为 STM32F1 系列的串口 1 引脚序号都是一样的,这样本身就便于在移植时不做任何 GPIO 定义的更改。 下面是中断服务函数 USART1_IRQHandler,这里主要使用空闲中断,当接收到一帧数据后,总线空闲中断后,使用读取 USART1->SR 的方式再读取 DR 的方式来清除 IDLE 标记,同时将 ReceiveState 标志置 1。 /**/ /******************************************************************************* * 函数名 :USART1_IRQHandler * 函数功能 :串口中断1中断服务函数 * 形参 : 无 * 返回值 : 无 *******************************************************************************/ /**/ void USART1_IRQHandler(void) { uint8_t Res=Res; //接收到一个字节数据 if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { USART_RX_BUF[RxCounter++]=USART1->DR; //将接收到的字节保存 } //接收到一帧数据 if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET) { Res=USART1->SR; //读SR寄存器 Res=USART1->DR; //读DR寄存器(先读SR再读DR可以清除IDLE) ReceiveState=1; //接收状态标志位 } } 下面是状态清零函数,这个函数比较简单,函数只是简单的将 RxCounter 和 ReceiveState 两个全局变量清零的操作做了一个封装,方便外部函数进行调用。 /**/ /******************************************************************************* * 函数名 :Uart0_STA_Clr * 函数功能 :串口状态清除函数 * 形参 : 无 * 返回值 : 无 *******************************************************************************/ /**/ void Uart0_STA_Clr(void) { RxCounter=0; //串口Buf计数清零 ReceiveState=0; //接收状态清零 } 有过 C 语言基础的用户,都知道一个格式化打印输出的函数 printf。在 STM32 里我们要使用 printf 的方法,必须做一些操作,这里给出了一种方法,通过代码的方式实现printf,而不需要使用 MicroLIB。注意,要使用下面的文件,有些关键词是在 stdio.h 里申明的,我们在 bsp.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 这里解释一下 0x40,0x40 是 USART_FLAG_TC 的标志。(USART1->SR&0X40)==0 这句和 USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET 这句功能是一样的,一种是寄存器的写法,一种是使用库函数的方法。而实际在底层都是最终操作的寄存器。 使用库函数进行数据发送的函数如下,这个函数主要是便于发送 16 进制数据的。 /**/ /******************************************************************************* * 函数名 :USART1_Send_Data * 函数功能 :USART1发送len个字 * 形参 : buf:发送区首地址 len:发送的字节数 * 返回值 : 无 *******************************************************************************/ /**/ void USART1_Send_Data(uint8_t *buf,uint8_t len) { uint8_t t; for(t=0;t while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); USART_SendData(USART1,buf[t]); } while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); } 另外介绍一下 bsp_uart.h 里的内容,特别注意一下 extern 关键词。 #ifndef __BSP_UART_H #define __BSP_UART_H #include "sys.h" #define USART_REC_LEN 1024 //定义最大接收字节数 1024 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口 1 接收 extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大 USART_REC_LEN 个字节.末字节为换行符 extern uint8_t ReceiveState; //接收状态标记 extern uint16_t RxCounter; void bsp_InitUart1(uint32_t baud); #if EN_USART1_RX //如果使能了接收 void Uart0_STA_Clr(void); #endif #endif 如果某个 C 文件中定义的全局变量,需要被其它 C 文件使用时,函数实体定义在 C 文件里,而在.h 头文件里用 extern 申明该变量,且在申明时不能对该变量赋值,其它 C 文件调用时用 include 包含该头文件即可。 7.3主函数 /* ********************************************************************************************************* * * 模块名称 : 主程序入口 * 文件名称 : main.c * 版 本 : V1.1 * 说 明 : 串口例子 ********************************************************************************************************* */ #include "bsp.h" /* 底层硬件驱动 */ /* ********************************************************************************************************* * 函 数 名: main * 功能说明: c程序入口 * 形 参:无 * 返 回 值: 错误代码(无需处理) ********************************************************************************************************* */ int main(void) { uint8_t Key_Code; //按键代码 uint16_t t; uint16_t len; //硬件初始化 bsp_Init(); printf("DS0 正在闪烁(闪烁频率 = 1Hz)rn"); printf("操作按键会打印按键事件rn"); //启动软件定时器1自动重装500ms bsp_StartAutoTimer(0, 500); /* 启动 1 个 500ms 的自动重装的定时器 */ while(1) { //喂狗 bsp_Idle(); /*LED*/ if(bsp_CheckTimer(0)) { bsp_LedToggle(1); printf("请输入数据,以回车键结束rn"); } /*按键处理*/ Key_Code=bsp_GetKey(); if(Key_Code!=KEY_NONE) { switch(Key_Code) { case WKUP_DOWN: //WKUP按下Led1亮,串口打印 bsp_LedOn(1); printf("WKUP键按下rn"); break; case WKUP_UP: //WKUP抬起,串口打印 printf("WKUP键抬起rn"); break; case KEY0_DOWN: //KEY0按下,串口打印 printf("KEY0键按下rn"); break; case KEY0_UP: //KEY0抬起 ,串口打印 printf("KEY0键抬起rn"); break; default : break; } } //若收到数据 if(ReceiveState) { len=RxCounter; //得到此次接收数据的长度 printf("rn发送的消息为rnrn"); for(t=0;t USART_SendData(USART1,USART_RX_BUF[t]); while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); } printf("rnrn"); Uart0_STA_Clr(); } } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1801 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1684 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
745浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
579浏览 3评论
601浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 13:10 , Processed in 0.847113 second(s), Total 80, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号