资源描述
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 file you are using is */
/* standard output using printf() for debugging,no file handling */
/* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __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=7000/(9600*16)=468.75
那么得到:
DIV_Fraction=16*0.75=12=0X0C;
DIV_Mantissa= 468=0X1D4;
这样,咱们就得到了USART1->BRR值为0X1D4C。只要设立串口1BRR寄存器值为
0X1D4C就可以得到9600波特率。
固然,并不是任何条件下都可以随便设立串口波特率,在某些波特率和PCLK2频率下,还是会存在误差,详细可以参照《STM32参照手册》第525页表176。
接下来,咱们就可以初始化串口了,需要注意是这里初始化串口是按8位数据格式,1位停止位,无奇偶校验位。
RCC->APB2ENR|=1<<14; //使能串口时钟
寄存器位如下:
位14
USART1EN:USART1时钟使能
由软件置’1’或清’0’
0:USART1时钟关闭;
1:USART1时钟启动。
GPIOA->CRH&=0XFFFFF00F;
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 */
展开阅读全文