STM32学习笔记-USART程序解释(原子)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
USART程序分析
一 .H文件
#ifndef __USART_H
#define __USART_H
#include <stm32f10x_lib.h>
#include "stdio.h"
extern u8 USART_RX_BUF[64]; //接收缓冲,最大63个字节.末字节为换行符extern u8 USART_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
//#define EN_USART1_RX 使能串口1接收
void uart_init(u32 pclk2,u32 bound);
#endif
解释:extern 作用域:如果整个工程由多个文件组成,在一个文件中想引用另外一个文件中已经定义的外部变量时,则只需在引用变量的文件中用extern关键字加以声明即可。
可见,其作用域从一个文件扩展到多个文件了。
例子:
文件a.c的内容:
#include <stdio.h>
int BASE=2; //变量定义
int exe(int x); //外部函数提前声明
int main(int argc, char *agrv[])
{
int a=10;
printf("%d^%d = %d\n",BASE,a,exe(a));
return 0;
}
文件b.c的内容:
#include <stdio.h>
extern BASE; //外部变量声明
int exe(int x)
{
int i;
int ret=1;
for(i=0;i<x;i++)
{
ret*=BASE;
}
return ret;
}
利用gcc工具编译gcc a.c b.c –o demo,再运行./demo,结果为2^10
= 1024。
其中,在a.c文件中定义BASE=2,在b.c中引用BASE时,需要用extern关键字声明其为外部变量,否则编译会找不到该变量。
二 .C文件
#include "sys.h"
#include "usart.h"
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
/* Whatever you require here. If the only are using is */
/* standard output using printf() for debugging, no */
/* is required. */
};
/* typedef’ d in stdio.h. */
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
解释:最后这里就是定义printf的输出执行单元了,比如现在是串口1输出,如果你要串口2,那么设置USART1为USART2即可。
#ifdef EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[64]; //接收缓冲,最大64个字节.
//接收状态
//bit7,接收完成标志
//bit6,接收到0x0d
//bit5~0,接收到的有效字节数目
u8 USART_RX_STA=0; //接收状态标记
void USART1_IRQHandler(void)
{
u8 res;
if(USART1->SR&(1<<5))//接收到数据
{
res=USART1->DR;
if((USART_RX_STA&0x80)==0)//接收未完成
{
if(USART_RX_STA&0x40)//接收到了0x0d
{
if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x80; //接收完成了
}else //还没收到0X0D
{
if(res==0x0d)USART_RX_STA|=0x40;
else
{
USART_RX_BUF[USART_RX_STA&0X3F]=res;
USART_RX_STA++;
if(USART_RX_STA>63)USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
#endif
解释:
void USART1_IRQHandler(void)函数是一个串口1 中断响应函数,当串口1 发生了相应的中断后,就会跳到该函数执行。
这里我们设计了一个小小的接收协议:通过这个函数,配合一个数组USART_RX_BUF[64],一个接收状态寄存器USART_RX_STA 实现对串口数据的接收管理。
USART_RX_BUF 的最大值为64,也就是一次接收的数据最大不能超过64 个字节。
USART_RX_STA 是一个接收状态寄存器其各的定义如下表:
设计思路如下:
当接收到从电脑发过来的数据,把接收到的数据保存在USART_RX_BUF 中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(0X0D,0X0A)的第一个字节0X0D 时,计数器将不再增加,等待0X0A 的到来,而如果0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。
如果顺利接收到0X0A,则标记USART_RX_STA的第七位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到0X0D,那么在接收数据超过64 个了,则会丢弃前面的数据,重新接收。
USART1->SR的第5位:
USART1->DR
通过上述分析,程序便可以理解。
//初始化IO 串口1
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率
//CHECK OK
//091209
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
RCC->APB2ENR|=1<<14; //使能串口时钟
GPIOA->CRH&=0XFFFFF00F;
GPIOA->CRH|=0X000008B0;//IO状态设置
RCC->APB2RSTR|=1<<14; //复位串口1
RCC->APB2RSTR&=~(1<<14);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置
USART1->CR1|=0X200C; //1位停止,无校验位.
#ifdef EN_USART1_RX //如果使能了接收
//使能接收中断
USART1->CR1|=1<<8; //PE中断使能
USART1->CR1|=1<<5; //接收缓冲区非空中断使能
MY_NVIC_Init(2,3,USART1_IRQChannel,2);//组2,最低优先级
#endif
}
解释:STM32的每个串口都有一个自己独立的波特率寄存器USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。
其各位描述如下图所示:
前面提到STM32的分数波特率概念,其实就是在这个寄存器(USART_BRR)里面体现的。
USART_BRR的最低4位(位[3:0])用来存放小数部分DIV_Fraction,紧接着的12位(位[15:4])用来存放整数部分DIV_Mantissa,最高16位未使用。
这里,我们简单介绍一下波特率的计算,STM32的串口波特率计算公式如下:
上式中,是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1);USARTDIV是一个无符号定点数。
我们只要得到USARTDIV的值,就可以得到串口波特率寄存器USART1->BRR的值,反过来,我们得到USART1->BRR的值,也可以推导出USARTDIV的值。
但我们更关心的是如何从USARTDIV的值得到USART_BRR的值,因为一般我们知道的是波特率,和PCLKx的时钟,要求的就是USART_BRR的值。
下面我们来介绍如何通过USARTDIV得到串口USART_BRR寄存器的值。
假设我们的串口1要设置为9600的波特率,而PCLK2的时钟为72M。
这样,我们根据上面的公式有:
USARTDIV=72000000/(9600*16)=468.75
那么得到:
DIV_Fraction=16*0.75=12=0X0C;
DIV_Mantissa= 468=0X1D4;
这样,我们就得到了USART1->BRR的值为0X1D4C。
只要设置串口1的BRR寄存器值为
0X1D4C就可以得到9600的波特率。
当然,并不是任何条件下都可以随便设置串口波特率的,在某些波特率和PCLK2频率下,还是会存在误差的,具体可以参考《STM32参考手册》的第525页的表176。
接下来,我们就可以初始化串口了,需要注意的是这里初始化串口是按8位数据格式,1位停止位,无奇偶校验位的。
RCC->APB2ENR|=1<<14; //使能串口时钟
GPIOA->CRH|=0X000008B0;//IO状态设置
IO设置成上啦或下拉模式,一个输入一个输出。
RCC->APB2RSTR|=1<<14; //复位串口1
RCC->APB2RSTR&=~(1<<14);//停止复位
具体查看RCC->APB2RSTR寄存器的定义。
USART1->CR1|=0X200C; //1位停止,无校验位.
USART1->CR1|=1<<8; //PE中断使能
USART1->CR1|=1<<5; //接收缓冲区非空中断使能
参考控制寄存器1(USART_CR1) 参考手册496页。
MY_NVIC_Init(2,3,USART1_IRQChannel,2);//组2,最低优先级
USART1_IRQChannel是中断编号。
//#define USART1_IRQChannel ((u8)0x25) /* USART1 global Interrupt */。