串口通信虽然在如今的电脑上使用的越来越少,因为其在通信速率,距离已经不适应pc的要求,取而代之的是USB口。但是在嵌入式领域,USART仍然广泛运用着。
stm32的最多可以提供5路串口,有分数波特率发生器、支持同步单线通信和半双工单线通信、具有DMA等。使用USART时,stm32的I/O口经RS232电平转换电路 和电脑的串口连接。
串口使用只需要开始串口时钟,设置相应的I/O口模式,配置波特率、数据位长度、奇偶校验位等信息就可以使用了。
我使用了三种方式使用串口通信,只可以开启一项:
-
USART通过使用printf()函数发送信息;
-
USART和上位机通信,接收到数据后原数据输出;
-
USART主动发送数据。
操作寄存器
串口的复位是通过配置APB2RSTR 寄存器的第14位,当外设出现故障时,可以通过复位寄存器复位,在系统初始化时,都会执行复位操作。
串口的波特率设置是在USART_BRR寄存器上, 实际上这个寄存器配置的是波特比率的分频触发因子的值,波特率是一秒钟通过的字符,而波特比率是一秒钟通过的二进制位数,所以设置了波特率需要经过一段算法处理 ,得出特定时钟下,实现这个波特率的,时钟分频值。
串口控制寄存器有3个 USART_CR1~3,常用到的就是USART_CR1,各位描述如下:
UE:USART使能 (USART enable)
M:字长 (Word length) 该位定义了数据字的长度, 0:一个起始位,8个数据位,n个停止位;
1:一个起始位,9个数据位,n个停止位。 n由USART_CR2中设置。
WAKE:唤醒的方法 (Wakeup method) 0:被空闲总线唤醒; 1:被地址标记唤醒。
PCE:检验控制使能 (Parity control enable)
PS:校验选择 (Parity selection) 0:偶校验;1:奇校验。
PEIE:PE中断使能 (PE interrupt enable)
TXEIE:发送缓冲区空中断使能 (TXE interrupt enable)
TCIE:发送完成中断使能 (Transmission complete interrupt enable)
RXNEIE:接收缓冲区非空中断使能 (RXNE interrupt enable)
IDLEIE:IDLE中断使能 (IDLE interrupt enable) 0:禁止产生中断; 1:当USART_SR中的IDLE为’1’时,产生USART中断。
TE:发送使能 (Transmitter enable)
RE:接收使能 (Receiver enable)
RWU:接收唤醒 (Receiver wakeup) 0:接收器处于正常工作模式; 1:接收器处于静默模式。
注意:1.在把USART置于静默模式(设置RWU位)之前,USART要已经先接收了一个数据字节。否则在静默模式下,不能被空闲总线检测唤醒。
2.当配置成地址标记检测唤醒(WAKE位=1),在RXNE位被置位时,不能用软件修改RWU位。
SBK:发送断开帧 (Send break)
数据的发送和接收是在USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR,当向该寄存器写入数据时,串口就会自动发送数据;当收到数据时,也是存在该寄存器内中,可以直接读出。该寄存器只有低9位有效(8:0),其他位都是保留的。
串口状态是通过状态寄存器USART_SR读取的,各位描述如下:
TXE:发送数据寄存器空 (Transmit data register empty)
当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
TC:发送完成 (Transmission complete)
当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。
RXNE:读数据寄存器非空 (Read data register not empty)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位,表示已经接收到了数据。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
直接操作寄存器代码如下:(system.h 和 stm32f10x_it.h 等相关代码参照 )
User/main.c
06
|
#define PRINTF_ON 0 //设置printf() 输出
|
07
|
#define RECEIVE_SEND_BACK 0 //设置接收信息后原文发送
|
08
|
#define SEND_WORDS 1 //设置发送字符串
|
12
|
vu8 RxBuffer[]="\r\n i will success finally..\r\n";
|
20
|
Usart1_Init(72,9600); //设置系统时钟和波特率
|
23
|
Nvic_Init(3,3,USART1_IRQChannel,2);
|
26
|
Gpio_Init(); //配置串口寄存器时可能导致IO口输出,最后配置最好
|
29
|
printf("\r\nThanks god ,i am success now..\r\n");
|
31
|
while(sizeof(RxBuffer)>=i)
|
33
|
USART1->DR = RxBuffer[i];
|
34
|
while((USART1->SR&0x40) == 0);
|
35
|
//USART1->SR &= 0x1F; //清除TC中断
|
47
|
RCC->APB2ENR|=1<<2; //使能PORTA时钟
|
49
|
//GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
|
50
|
//GPIOA->CRL|=0x33334444;
|
54
|
GPIOA -> CRH&=0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
|
55
|
GPIOA -> CRH|=0x000008B0;
|
User/stm32f10x_it.c
01
|
#include "stm32f10x_it.h"
|
03
|
void USART1_IRQHandler(void)
|
07
|
if(USART1->SR &(1<<5)) //接收到数据
|
11
|
while((USART1->SR&0x40) == 0);
|
Library/src/usart.c
05
|
* 初始化 USART1的控制寄存器、波特比率寄存器、开启时钟
|
06
|
* 注意:未配置I/O口功能,需设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
|
09
|
void Usart1_Init(u32 clk,u32 baudRate) //参数说明: clk 单位为Mhz
|
18
|
temp = (float)(clk*1000000)/(baudRate*16); //得到USART 分频除法因子(每个字符16位,乘16得到每秒通过的字符数)
|
20
|
BRR_Mantissa = temp; //得到BRR[15:4]整数部分
|
22
|
BRR_Fraction = (temp - BRR_Mantissa)*16; //得到BRR[3:0]小数部分
|
26
|
BRR_Value = BRR_Mantissa + BRR_Fraction; //拼接整数和小数部分
|
28
|
RCC->APB2ENR|=1<<14; //使能串口时钟
|
30
|
RCC->APB2RSTR |= 1<<14; //复位串口1
|
31
|
RCC->APB2RSTR &= ~(1<<14); //初始化串口复位寄存器位
|
33
|
USART1->BRR = BRR_Value; //设置波特比率
|
35
|
USART1->CR1 |= 1<<8; //PEIE中断使能
|
36
|
USART1->CR1 |= 1<<5; //RXNEIE,接收完成中断使能
|
37
|
//USART1->CR1 |= 1<<6; //TC,发送完成中断使能
|
39
|
USART1->CR1 |= 0x200C; //1个停止位,无检验位
|
46
|
#pragma import(__use_no_semihosting)
|
51
|
/* Whatever you require here. If the only file you are using is */
|
52
|
/* standard output using printf() for debugging, no file handling */
|
55
|
/* FILE is typedef’ d in stdio.h. */
|
57
|
//定义_sys_exit()以避免使用半主机模式
|
63
|
int fputc(int ch, FILE *f)
|
66
|
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
|
Library/inc/usart.h
03
|
#define PRINTF_OUT 1 //使用printf()方式输出,使用这种方式会增大hex文件大小,所以不适用这种方式输出就置0
|
10
|
void Usart1_Init(u32 clk,u32 baudRate);
|
在MDK下 将上述文件添加到工程对应目录下即可:
库函数操作
在使用printf()串口输出 MDK 必须配置为User Micro LIB 。即为动态编译,可以减小 hex文件的带下 减小flash使用空间。
代码如下:
001
|
#include "stm32f10x.h"
|
004
|
#define PRINTF_ON 0 //设置printf() 输出
|
005
|
#define RECEIVE_SEND_BACK 0 //设置接收信息后原文发送
|
006
|
#define SEND_WORDS 1 //设置发送字符串
|
008
|
vu8 RxBuffer[]="\r\n i will success finally..\r\n"; //存储串口传入的数据,自定义字符串
|
011
|
void RCC_Configuration(void);
|
012
|
void GPIO_Configuration(void);
|
013
|
void USART_Configuration(void);
|
020
|
GPIO_Configuration();
|
021
|
USART_Configuration();
|
025
|
printf("\r\nThanks god ,i am success now..\r\n");
|
026
|
#elif RECEIVE_SEND_BACK
|
027
|
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
|
029
|
RxBuffer[count] = USART_ReceiveData(USART1);
|
030
|
USART_SendData(USART1,RxBuffer[count]);
|
035
|
USART_SendData(USART1,RxBuffer[count]);
|
037
|
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
|
038
|
//while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); //检查是否发送完成的另一种方法
|
054
|
void delay(vu32 x) //vu32 1us一次
|
061
|
void GPIO_Configuration(void)
|
063
|
GPIO_InitTypeDef GPIO_InitStructure;
|
065
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
|
066
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
067
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
068
|
GPIO_Init(GPIOA , &GPIO_InitStructure);
|
070
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
|
071
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
072
|
GPIO_Init(GPIOA , &GPIO_InitStructure);
|
076
|
void RCC_Configuration(void)
|
078
|
/* 定义枚举类型变量 HSEStartUpStatus */
|
079
|
ErrorStatus HSEStartUpStatus;
|
084
|
RCC_HSEConfig(RCC_HSE_ON);
|
086
|
HSEStartUpStatus = RCC_WaitForHSEStartUp();
|
087
|
/* 判断HSE起是否振成功,是则进入if()内部 */
|
088
|
if(HSEStartUpStatus == SUCCESS)
|
090
|
/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
|
091
|
RCC_HCLKConfig(RCC_SYSCLK_Div1);
|
092
|
/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
|
093
|
RCC_PCLK2Config(RCC_HCLK_Div1);
|
094
|
/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
|
095
|
RCC_PCLK1Config(RCC_HCLK_Div2);
|
097
|
FLASH_SetLatency(FLASH_Latency_2);
|
099
|
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
|
100
|
/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
|
101
|
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
|
105
|
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
|
106
|
/* 选择SYSCLK时钟源为PLL */
|
107
|
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
|
108
|
/* 等待PLL成为SYSCLK时钟源 */
|
109
|
while(RCC_GetSYSCLKSource() != 0x08);
|
111
|
/* 打开APB2总线上的GPIOA时钟*/
|
112
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
|
114
|
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
|
119
|
void USART_Configuration(void)
|
121
|
USART_InitTypeDef USART_InitStructure;
|
122
|
USART_ClockInitTypeDef USART_ClockInitStructure;
|
124
|
USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
|
125
|
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
|
126
|
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
|
127
|
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
|
128
|
USART_ClockInit(USART1 , &USART_ClockInitStructure);
|
130
|
USART_InitStructure.USART_BaudRate = 9600;
|
131
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
132
|
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
133
|
USART_InitStructure.USART_Parity = USART_Parity_No;
|
134
|
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
135
|
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
|
136
|
USART_Init(USART1,&USART_InitStructure);
|
138
|
USART_Cmd(USART1,ENABLE);
|
145
|
int fputc(int ch,FILE *f)
|
147
|
USART_SendData(USART1,(u8) ch);
|
148
|
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
|
阅读(2940) | 评论(0) | 转发(0) |