stm32寄存器版学习笔记02 串口通信
stm32F103RCT6提供5路串口。串口的使用,只要开启串口时钟,设置相应的I/O口的模式,然后配置下波特率、数据位长度、奇偶校验等信息,即可使用。
1.串口的配置步骤
①串口时钟使能
APB2外设时钟使能寄存器(RCC_APB2ENR)
置1开启。清0关闭。
第14位对串口1的时钟使能
Eg:RCC->APB2ENR| = 1<<14; //使能串口1时钟
除串口1的时钟使能在RCC_APB2ENR寄存器,其余的时钟使能位在寄存器RCC_APB1ENR寄存器,而APB2(72M)的频率一般是APB1(36M)的一倍。
APB1外设时钟使能寄存器(RCC_APB1ENR)
20-17位 串口5-2时钟使能
Eg:RCC->APB1ENR| = 1<<17; //使能串口2时钟
②串口复位
一般在系统刚开始配置外设时,都会先执行复位该外设的操作,而复位后要将其结束复位。
串口复位主要在寄存器RCC_APB2RSTR(串口1的复位)和寄存器RCC_APB1RSTR(串口2-5的复位)。
APB2外设复位寄存器 (RCC_APB2RSTR)
置1复位,清0无作用。
第14位是串口1复位
Eg:RCC->APB2RSTR |= 1<<14; //复位串口1
RCC->APB2RSTR &= ~(1<<14); //停止复位
APB1外设复位寄存器 (RCC_APB1RSTR)
置1复位,清0无作用。
20-17位 串口5-2复位
Eg: RCC->APB1RSTR |= 1<<17; //复位串口2
RCC->APB1RSTR &= ~(1<<17); //停止复位
③串口波特率设置
波特比率寄存器()
关于波特率设置在函数void uart_init(u32 pclk2,u32 bound)里已经设置好,并且封装在usart.c文件里面可以直接调用。
④串口控制
stm32的每个串口都有3个控制寄存器USART_CR1~3控制
控制寄存器1(USART_CR1)
该寄存器32~14位保留,第13位使能串口(任何串口在应用的时候都必需将其置“1”)第12位设置字长,当这位为“0”的时候设置串口位8个字长外加n个停止位,这n个停止位在寄存器USART_CR2中第[13:12]位来决定。PCE为奇偶校验使能位设置为“0”则禁止校验,否则使能校验。PS是交验选择位,设置为“0”则为偶校验,否则为奇校验。PEIE:PE(校验错误)中断使能,该位由软件设置或清除,定义:0(禁止产生中断),1(当USART_SR中的PE为’1’时,产生USART中断)。TXEIE发送缓冲区空中断使能,(手动),定义:0(禁止产生中断),1(当USART_SR中的TXE为’1’时,产生USART中断)。TCIE发送完成中断使能,(手动),定义:0(禁止产生中断)1(当USART_SR中的TC为’1’时,产生USART中断)。RXNEIE接收缓冲区非空中断使能,(手动),定义:0(禁止产生中断),1(当USART_SR中的ORE或者RXNE为’1’时,产生USART中断)。TE为发送使能位,设置为“1”将开启串口的发送功能。RE为接收使能位,用法同TE。
控制寄存器2(USART_CR2)
Eg:USART1->CR1|=0X200C; //1位停止,无校验位. 0X200C=0010 0000 0000 1100B
设置成使能串口8个字长1个停止位(USART_CR2中[13:12]默认为“0”)禁止校验,禁止校验所有中断,使能发送和接收。
⑤数据的发送和接收
数据寄存器(USART_DR)
发送数据缓存寄存器(向它写数据它会自动发送数据),当接收到数据时则存放接收的数据
⑥串口状态
状态寄存器(USART_SR)
RXNE(读数据寄存器非空),当该位被置1时,即提示已经有数据被接收,可以读取。我们应尽快读取USART_DR,通过读USART_DR可以将该位清0,也可以向该位写0直接清除。
TC(发送完成),当该位被置位时,表示USART_DR内的数据以及被发送完成了。如果设置了这个位的中断,则会产生中断。该位两种清0方式:①读USART_DR ②向该位写0直接清除
2.关于波特率的计算
void uart_init(u32 pclk2,u32 bound) pclk2是系统时钟平率。bound需要设置的波特率,例如9600、115200等。
参考1.③中的USART_BRR寄存器。
STM32串口波特率的计算公式如下:
Tx/Rx波特率 = fPCLKx / (16*USARTDIV)
fPCLKx是给串口的时钟(PCLK1用于USART2-5,PCLK2用于USART1)
USARTDIV是一个无符号定点数,得到USARTDIV,可得USART1->BRR值;可得USART1->BRR值,可推USARTDIV。
Eg:串口1要设置为9600波特率,而PCLK2时钟为72MHz。
USARTDIV = 72000000 / (16 * 9600) = 468.75
那么得到:DIV_Fraction(小数部分) = 16*0.75 = 12 = 0x0C; DIV_Mantissa(整数部分) = 468 = 0x1D4
这样就得到了USART1->BRR = 0x1D4C。设置1.③中的USART_BRR寄存器值为0x1D4C,即可得到9600的波特率。
3.USART1
void uart_init(u32 pclk2,u32 bound)
void USART1_IRQHandler(void)
两个函数已经封装在usart.c中可直接调用
//初始化I/O 串口1
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率 void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*)/(bound*);//得到USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*; //得到小数部分
mantissa<<=;
mantissa+=fraction;
RCC->APB2ENR|=<<; //使能PORTA时钟
RCC->APB2ENR|=<<; //使能串口时钟
GPIOA->CRH&=0XFFFFF00F;//IO状态设置 PA9 PA10
GPIOA->CRH|=0X000008B0;//IO状态设置 RCC->APB2RSTR|=<<; //复位串口1
RCC->APB2RSTR&=~(<<);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置
USART1->CR1|=0X200C; //1位停止 无校验位
#if EN_USART1_RX //如果使能了接收
//使能接收中断
USART1->CR1|=<<; //PE中断使能
USART1->CR1|=<<; //接收缓冲区非空中断使能
MY_NVIC_Init(,,USART1_IRQn,);//组2 最低优先级
#endif
}
当需要使用串口接收的时候,要在usart.h里面设置EN_USART1_RX为1即可。不使用时设置为0。
//uart.h
#ifndef __USART_H
#define __USART_H
#include "sys.h"
#include "stdio.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 pclk2,u32 bound); #endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=; //接收状态标记 void USART1_IRQHandler(void)
{
u8 res;
#ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
OSIntEnter();
#endif
if(USART1->SR&(<<))//接收到数据
{
res=USART1->DR;
if((USART_RX_STA&0x8000)==)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(res!=0x0a)USART_RX_STA=;//接收错误,重新开始
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-))USART_RX_STA=;//接收数据错误,重新开始接收
}
}
}
}
#ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了.
OSIntExit();
#endif
}
#endif
4.关于void USART1_IRQHandler(void)函数
该函数时串口1的中断响应函数,当串口1发生相应中断后,就会跳到该函数执行。这里设计的接收协议:通过这个函数,配合一个数组USART_RX_BUF[]、一个接收状态寄存器USART_RX_STA(全局变量,作者自行添加)实现对串口数据的接受管理。USART_RX_BUF[]大小由USART_REC_LEN定义,即不超过USART_REC_LEN个字节。
5.应用测试
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h" int main(void)
{
u8 t;
u8 len;
u16 times=;
Stm32_Clock_Init(); //系统时钟设置
delay_init(); //延时初始化
uart_init(,); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
while()
{
if(USART_RX_STA&0x8000) //1000 0000 0000 0000 接收成功
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
for(t=;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=;
}else
{
times++;
if(times%==)printf("请输入数据,以回车键结束\r\n");
if(times%==)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms();
}
}
}
stm32寄存器版学习笔记02 串口通信的更多相关文章
- stm32寄存器版学习笔记07 ADC
STM32F103RCT有3个ADC,12位主逼近型模拟数字转换器,有18个通道,可测量16个外部和2个内部信号源.各通道的A/D转换可以单次.连续.扫描或间断模式执行. 1.通道选择 stm32把A ...
- stm32寄存器版学习笔记06 输入捕获(ETR脉冲计数)
STM32外部脉冲ETR引脚:TIM1-->PA12;TIMER2-->PA0:TIMER3-->PD2;TIMER4-->PE0… 1.TIM2 PA0计数 配置步骤 ①开启 ...
- stm32寄存器版学习笔记05 PWM
STM32除TIM6和TIM7外都可以产生PWM输出.高级定时器TIM1和TIM8可以同时产生7路PWM,通用定时器可以产生4路PWM输出. 1.TIM1 CH1输出PWM配置步骤 ①开启TIM1时钟 ...
- stm32寄存器版学习笔记03 外部中断
stm32的每个I/O口都可以作为中断输入,要把I/O口设置为外部中断输入,必须将I/O口设置为上拉/下拉输入 或 浮空输入(但浮空的时候外部一定要带上拉或下拉电阻,否则可能导致 中断不停的触发),干 ...
- stm32寄存器版学习笔记08 DMA
DMA(Direct Memory Access),直接存储器访问.DMA传输方式无需CPU直接控制传输,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU效率大大提高.stm32f10 ...
- stm32寄存器版学习笔记04 定时计数器中断
STM32共有8个定时计数器,其中TIME1和TIME8是高级定时器,TIME2~TIME5是通用定时器,TIME6和TIME7是基本定时器.以TIME3为例总结定时计数器的基本用法. 1.TIM3的 ...
- stm32寄存器版学习笔记10 SPI
SPI(Serial Peripheral Interface),串行外围设备接口.SPI是一种高速的.全双工.同步的通信总线. SPI接口一般使用4条线通信: MISO 主设备数据输入,从设备数据输 ...
- stm32寄存器版学习笔记01 GPIO口的配置(LED、按键)
STM32的I/O口可以由软件配置成如下8种模式:输入浮空.输入上拉.输入下拉.模拟输入.开漏输出.推挽输出.推挽式复用功能及开漏复用功能.每个I/O口由7个寄存器来控制:配置模式的端口配置寄存器CR ...
- stm32寄存器版学习笔记09 IIC
I²C(Inter-Integrated Circuit)总线是一种两线式串行总线,用于连接微控制器及其外设,是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据. IIC总线在传送数据过程中 ...
随机推荐
- 使用FireFox插件RESTClient工具POST方法?
下面尝试用Firefox的restclient,来调取api 当然需要打开火狐浏览器安装restclient的插件https://addons.mozilla.org/en-US/firefox/ad ...
- java高级特性(1)--理解面向对象思想
前言: 优秀的代码具备:高性能.可重用.可扩展.易维护.易理解 具体实现: 高性能:合理使用算法,数据结构等等 可重用:封装.继承 可扩展:多态 易维护.易理解:命名规范 + 注解 面向对象是一种思想 ...
- java, double转String, 去掉0结尾的小数位
小问题:double值的小数位是0时,转String会有“.0”结尾.比如,double值是“12”,转String得到的字符串是“12.0”.如果需要去掉0结尾的小数位,应当如何解决呢? 解决方案: ...
- nyoj299——如何优雅的写矩阵快速幂
Matrix Power Series 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 Given a n × n matrix A and a positive i ...
- nyoj42——连通图加欧拉(连通图板子)dfs
一笔画问题 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...
- tcpdump 使用实例
详细的文档见tcpdump高级过滤技巧 基本语法 ========过滤主机--------- 抓取所有经过 eth1,目的或源地址是 192.168.1.1 的网络数据# tcpdump -i eth ...
- javascript curry 柯里化函数 仿lodash的curry
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HTML5:了解Polyfills
利用 HTML5 来搭建网站和应用可能是一项艰巨的任务.尽管现在越来越多的现代浏览器正在更多的支持Html5新特性,但实际上只有很少部分人能够幸运的只需要为这些最新的浏览器编写代码.作为一个专业的开发 ...
- Java开发微信公众号模板消息【同步|异步】
第一步:申请模板消息功能并添加模板 在微信公众平台找到你需要的模板,并添加上即可: 第二步:添加功能模块后开始开发 功能中使用的类及代码: 发送数据主实体类: Template.java packag ...
- opencv:图像的基本变换
0.概述 图像变换的基本原理都是找到原图和目标图的像素位置的映射关系,这个可以用坐标系来思考,在opencv中, 图像的坐标系是从左上角开始(0,0),向右是x增加方向(cols),向下时y增加方向( ...