基于STM32F103ZET6库函数串口实验
基于STM32F103ZET6库函数串口实验
- 本次实验实现如下功能
- 串口设置的一般步骤可以总结为如下几个步骤
- 与串口基本配置直接相关的几个固件库函数
- 1.串口时钟使能。
- 2.串口复位。
- 3.串口参数初始化。
- 4..数据发送与接收。
- 5.串口状态。
- 6.串口使能。
- 7.开启串口响应中断。
- 8.获取相应中断状态。
- 硬件设计
- 软件设计
本次实验实现如下功能
STM32F1 通过串口和上位机的对话,STM32F1 在收到上位机发过来的字符串后,原原本本的返回给上位机。
接下来主要从库函数操作层面结合寄存器的描述,告诉你如何设置串口,以达到最基本的通信功能。 利用串口 1 不停的打印信息到电脑上,同时接收从串口发过来的数据,把发 送过来的数据直接送回给电脑。通过 USB 串口和电脑通信。
串口设置的一般步骤可以总结为如下几个步骤
- 串口时钟使能,GPIO 时钟使能
- 串口复位
- GPIO 端口模式设置
- 串口参数初始化
- 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
- 使能串口
- 编写中断处理函数
与串口基本配置直接相关的几个固件库函数
这些函数和 定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。
1.串口时钟使能。
串口是挂载在 APB2 下面的外设,所以使能函数为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
2.串口复位。
当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置 这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外 设的操作。复位的是在函数 USART_DeInit()中完成:
void USART_DeInit(USART_TypeDef* USARTx);//串口复位
3.串口参数初始化。
串口初始化是通过 USART_Init()函数实现的
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
这个函数的第一个入口参数是指定初始化的串口标号,这里选择 USART1。
第二个入口参数是一个 USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用 来设置串口的一些参数。一般的实现格式为:
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); //初始化串口
4…数据发送与接收。
STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是 一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收 到数据的时候,也是存在该寄存器内。
STM32 库函数操作 USART_DR 寄存器发送数据的函数是:
通过该函数向串口寄存器 USART_DR 写入一个数据。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
STM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
5.串口状态。
在我们固件库函数里面,读取串口状态的函数是:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的 RXNE(读数据寄存器非空)以及 TC(发送完成)。
例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
我们要判断发送是否完成(TC),操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_TC);
这些标识号在 MDK 里面是通过宏定义定义的:
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
6.串口使能。
串口使能是通过函数 USART_Cmd()来实现的
USART_Cmd(USART1, ENABLE); //使能串口
7.开启串口响应中断。
有些时候当我们还需要开启串口中断,那么我们还需要使能串口中 断,使能串口中断的函数是:
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类 型有很多种。比如在接收到数据的时候(RXNE 读数据寄存器非空),我们要产生中断,那么我 们开启中断的方法是:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:
USART_ITConfig(USART1,USART_IT_TC,ENABLE);
8.获取相应中断状态。
当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄 存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这 个函数来判断到底是否是串口发送完成中断,方法是:
USART_GetITStatus(USART1, USART_IT_TC)
返回值是 SET,说明是串口发送完成中断发生。
硬件设计
1) 指示灯 DS0
2) 串口 1
本实验用到的串口 1 与 USB 串口并没有在 PCB 上连接在一起, 需要通过跳线帽来连接一下。这里我们把 P3 的 RXD 和 TXD 用跳线帽与 PA9 和 PA10 连接起来。
软件设计
//初始化 IO 串口 1
void uart_init(u32 bound)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/*1.使能USART1,GPIOA时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); /*2.串口复位,这里没有 *//*3.GPIO 端口模式设置 *///USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10 /*4.串口参数初始化 *///USART 初始化设置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/*5.初始化 NVIC *///Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器/*5.开启中断 */USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口接受中断/*6.使能串口 */USART_Cmd(USART1, ENABLE); //使能串口1 }
① 串口时钟使能,GPIO 时钟使能
② 串口复位
③ GPIO 端口模式设置
④ 串口参数初始化
⑤ 初始化 NVIC 并且开启中断
⑥ 使能串口
usart.c
#include "sys.h"
#include "usart.h" //标准库需要的支持函数
struct __FILE
{ int handle;
};
FILE __stdout; //重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0){; //循环发送,直到发送完毕 }USART1->DR = (u8) ch; return ch;
} #if EN_USART1_RX //如果使能了接收u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记 void uart_init(u32 bound)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟/*USART1_TX GPIOA.9*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.9/*USART1_RX GPIOA.10*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器//USART 初始化设置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); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口接收中断USART_Cmd(USART1, ENABLE); //使能串口1 }/*串口1中断服务程序*/
void USART1_IRQHandler(void)
{u8 Res;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾){Res =USART_ReceiveData(USART1); //读取接收到的数据if((USART_RX_STA&0x8000)==0) //接收未完成{if(USART_RX_STA&0x4000) //接收到了0x0d{if(Res!=0x0a){USART_RX_STA=0; //接收错误,重新开始}else{USART_RX_STA|=0x8000; //接收完成了 }}else //还没收到0x0d{ if(Res==0x0d){ USART_RX_STA|=0x4000;}else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1)){USART_RX_STA=0; //接收数据错误,重新开始接收}} }} }
}
#endif
如果EN_USART1_RX等于1下面代码就执行
定义缓冲区和接收状态标记,接收标记为16位
0到13位为接收到的有效数目
14位为接收到0x0d的标记
15位为接收到0x0a的标记(即接收完成)
串口初始化这里按着步骤配置就好
下面来说一下串口1的中断服务函数
第一步读取接收的数据存到Res(这里是8位,每8位接收一次)
第二步如果还没读到0x0d就存进缓冲区USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;
USART_RX_STA&0X3FFF,这里限制只有用到0到13位
if(USART_RX_STA>(USART_REC_LEN-1))这里也限制了缓冲区的大小
第三步如果读到了0x0d就改变14位的状态USART_RX_STA|=0x4000;让14位置1
第四步接收到0x0d下一位就判断是否是0x0a,如果是就改变15位的状态USART_RX_STA|=0x8000;让15位置1,即接收完成
这里还有一个重定向函数int fputc(int ch, FILE *f)
此函数原本是将字符ch打印到文件指针stream所指向的文件流去的,现在我们不需要打印到文件流,而是打印到串口1
usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h" #define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记 void uart_init(u32 bound);
#endif
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
extern声明给其它的文件使用这些变量
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"int main(void)
{ u16 t; u16 len; u16 times=0;delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(115200); //串口初始化为115200LED_Init(); //LED端口初始化KEY_Init(); //初始化与按键连接的硬件接口while(1){if(USART_RX_STA&0x8000){ len=USART_RX_STA&0x3fff; //得到此次接收到的数据长度printf("\r\n您发送的消息为:\r\n\r\n");for(t=0;t<len;t++){USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); //等待发送结束}printf("\r\n\r\n"); //插入换行USART_RX_STA=0;}else{times++;if(times%5000==0){printf("\r\n串口实验\r\n");printf("是北豼不太皮吖\r\n\r\n");}if(times%200==0){printf("请输入数据,以回车键结束,或勾上发送新行\r\n");}if(times%30==0){LED0=!LED0; //闪烁LED,提示系统正在运行.}delay_ms(10); }}
}
if(USART_RX_STA&0x8000)判断是否接收完成
如果接收完成就把数据发回去
USART_RX_STA为传过来实际的字节数
len=USART_RX_STA&0x3fff;这里把14,15位的标记位去掉
基于STM32F103ZET6库函数串口实验相关推荐
- 基于STM32F103ZET6库函数外部中断实验
基于STM32F103ZET6库函数外部中断实验 外部中断简介 IO 口外部中断的一般步骤 软件设计 外部中断简介 通过板载的 3 个按键,控制板载的两个 LED 的亮灭以及蜂鸣器的发声. 代码主要分 ...
- 基于STM32F103ZET6库函数PWM输出实验
基于STM32F103ZET6库函数PWM输出实验 PWM 简介 硬件设计 软件设计 使 用 TIM3 的通道 2,把通道 2 重映射到 PB5,产生 PWM 来控制 DS0 的亮度. PWM 简介 ...
- 基于STM32F103ZET6库函数定时器中断实验
基于STM32F103ZET6库函数定时器中断实验 STM32F1 通用定时器简介 定时器相关的库函数主要集中在固件库文件 stm32f10x_tim.h 和 stm32f10x_tim.c 文件中 ...
- 基于STM32F103ZET6库函数按键输入实验
基于STM32F103ZET6库函数按键输入实验 GPIO_ReadInputDataBit函数 硬件设计 软件设计 我们将通过 ALIENTEK 精英 STM32F103 上载有的 3 个按钮(KE ...
- FPGA入门实验-基于状态机实现串口回环收发
任务目标 基于状态机实现串口回环收发.最近生产实习的FPGA培训课程内容,还是挺简单的.具体原理其他文章应该都烂大街了,重点是状态机的写法,还是很少博主写,没怎么看到,基本上都是时序机写的模块功能. ...
- 基于FPGA Uart串口通信实验
基于FPGA Uart串口通信实验 首先需要了解uart串口通信协议,根据个人专业需求不同,了解的层面可以不同. UART简介 通用异步收发传输器(Universal Asynchronous Rec ...
- 基于标准库函数与基于HAL库函数的stm32编程方式
文章目录 基于标准库函数与基于HAL库函数的stm32编程方式 一.基于标准库 1. 介绍 2. 库函数的STM32串口程序编写 二.基于HAL库 1.介绍 2.HAL库STM32串口程序编写 三.差 ...
- 基于标准库函数与基于HAL库函数的stm32编程方式对比
基于标准库函数与基于HAL库函数的stm32编程方式对比 一.标准库函数 二.HAL库函数 三.差异 四.stdunio IDE试玩 参考资料 一.标准库函数 1.标准库的解释 标准外设库(Stand ...
- STM32之USART-串口通信(含串口实验详细解析)
STM32之串口通信 - USART(含串口实验详细解析) 开发环境:Window 10 开发工具:Keil uVision5 MDK 硬件:STM32F103 资料参考: [正点原子]STM32F1 ...
最新文章
- 二叉搜索树的简明实现(ES5 ES6)
- android gpio驱动实例,Android/Linux 驱动层对GPIO口的操作方法和相关代码
- git 版本操作命令大全
- android 保存流媒体,Android实现使用流媒体播放远程mp3文件的方法
- Centos7 释放内存
- SAP Fiori Elements - When is catalog annotation $value loaded
- zookeeper介绍及集群的搭建(利用虚拟机)
- 互联网日报 | 2月23日 星期二 | 华为发布新一代折叠旗舰Mate X2;字节跳动游戏官网正式上线;携程设立轮值制度...
- sharepoint_study_5
- python 注销一大段_Django框架用户注销功能实现方法分析
- android 字体加粗_Android小技巧之TextView字体加粗
- 微型计算机汇编用什么软件,EMU8086 汇编工具软件的使用
- 使用REG注册表文件添加、修改或删除注册表键值和子项介绍
- android关闭触摸提示音,修改Android系统的触摸提示音【学习笔记】
- Java常见练习题总结
- Cloudera Manager 5升级(官方翻译整理,企业级实战验证)
- 将电脑文件夹内的文件名批量导入到Excel表格中
- 关于学生管理系统(注册,登录,学生端)
- 多个lmg在盒子里在左浮动( float: left)时候出现横向图片中间有缝隙
- svg文件解析(python)