我们之前学到的都只是对单片机简单的操作,单片机和上位机没有任何通信,等学完这一篇后,你就可以通过串口来和上位机进行联系,实现数据的交流。
一、串行通信
按照串行数据时钟控制,可以分为异步和同步通信。
1、异步
在异步通信中,数据是以字符为单位组成字符帧发送的,发送端和接收端可以由各自独立时钟来控制收发。在异步通信中,接收端通过字符帧格式来判断发送端的发送时间和结束时间。 (1)字符帧格式 起始位,数据位(低位在前),奇偶校验位,停止位`
-----------------------------------------------------------------
| 0 | D0 | D1 | D2 | D3| D4 | D5 | D6 | D7 | 0/1 | 1 |...|0|D0|..
-----------------------------------------------------------------
起始位:位于帧开头,占1位,为低电平0,表示发送端开始发送一帧数据 数据位:可取5位,6位,7位,8位,低位在前,高位在后 奇偶校验位:选择校验方式,有奇校验和偶校验 停止位:位于帧结尾,为高电平1,表示发送端发送完一帧数据
在串行通信中,相邻一帧数据可以有空闲位,也可以没有空闲位。 (2)波特率 每秒传送二进制数码的位数,也叫比特数,单位 bps;波特率和字符的实际传输速率不同,字符的实际传输速率是每秒内所传字符帧的帧数,和字符帧格式有关。 注意:在收发时,必须要保证波特率一致,就好比打电话,一个反应慢,另一个还说话快,这样在传递消息时,就会数据丢失,所以要设定波特率一致
异步通信的优点是不需要传送同步时钟,字符帧长度不受限制,设备简单,而缺点也很明显,起始位和停止位会大大降低传输效率。
2、同步
同步通信是一种连续串行传送数据的通信方式,一次通信只传输一帧信息。这里的信息帧通常有若干个数据字符,信息帧均由同步字符、数据字符和校验字符CRC三部分组成。在同步通信中,同步字符可以采用统一的标准格式,也可以自定义。
3、通信方式
通信方式有三种:单工,半双工,全双工。 单工:只能往一个方向传送
----- -----
| | -----------> | |
| | | |
----- -----
半双工:接收或发送要分时进行
----- -----
| | ----t1-----> | |
| | <---t2------ | |
----- -----
全双工:可以同时发送或接收
----- -----
| | ----t1-----> | |
| | <---t1------ | |
----- -----
二、USART
图中可以看到,STM32F103ZET6支持五路串口,有三个USART和两个UART。 USART:通用同步和异步收发器 UART:通用异步收发器 两者在做异步时没有任何区别,只不过USART是增强版,多了一个同步收发。
串口硬件连接: USART1:TX:PA9,RX:PA10 USART2:TX:PA2,RX:PA3 USART3:TX:PB10,RX:PB11 在使用串口时,这些GPIO口要设置为复用模式
三、USART工作原理
收:数据从RX口进入,先到接收移位寄存器,这个寄存器一位一位将数据移到接收数据寄存器RDR里,然后通过读操作读取数据。 发:先将数据存放到TDR数据寄存器里,再由移位寄存器发送给TX口。
四、USART寄存器
1、USART_SR(状态寄存器)
这个寄存器只介绍6位和5位
-
6位:TC:当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:(1)读 USART_SR,写USART_DR。(2)直接向该位写 0。 -
5位:RXNE:当该位被置 1 的时候,就是提示已经有数据被接收到了,并 且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
2、USART_DR(数据寄存器)
DR寄存器用来保存接收到的数据和要发送的数据,这是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送。
3、USART_BRR(波特率寄存器)
上文说的异步通信里最重要的波特率就是由这个寄存器设置的
- 15:4位:定义了USARTDIV的整数部分
- 3:0位:定义了USARTDIV的小数部分
我们常用的波特率为115200,那么计算方式如下: USARTDIV=72000000/(115200*16)=39.0625 整数部分:39 小数部分:0.0625 转换为16进制:整数部分:0x27,小数部分:0x1 那么 USART1_BRR=0x0271 ,就可以设置USART1串口的波特率为115200HZ。
4、USART_CR1(控制寄存器1)
- 13位:USART使能
- 12位:定义字长
- 6位:该位发送完成中断使能
- 5位:接收缓冲区非空中断使能
- 3位:发送使能
- 2位:接收使能
五、代码
1、usart.c
#include "stm32f10x.h"
#include "led.h"
#include "stdio.h"
#include "usart.h"
#include "delay.h"
void menu()
{
printf("\r\n-----------菜单--------\r\n");
printf("\r\n1、红灯翻转\r\n");
printf("\r\n2、绿灯翻转\r\n");
printf("\r\n3、蜂鸣器翻转\r\n");
printf("\r\n---请选择要进行的操作----\r\n");
}
void USART1_Init_NVIC(void)
{
RCC->APB2ENR|=1<<0;
SCB->AIRCR|=0x05FA0500;
NVIC->ISER[1]|=0x0000020;
NVIC->IP[37]|=15<<4;
}
void USART1_PUT(unsigned char C)
{
while((!(USART1->SR&(1<<6)))&&(!(USART1->SR&(1<<7))));
USART1->DR=C;
}
int fputc(int ch, FILE *f)
{
USART1_PUT(ch);
return ch;
}
void USART1_Init(void)
{
RCC->APB2ENR|=1<<2;
RCC->APB2ENR|=1<<14;
RCC->APB2RSTR|=1<<14;
RCC->APB2RSTR&=~(1<<14);
GPIOA->CRH&=0xFFFFF00F;
GPIOA->CRH|=0x00000490;
USART1->BRR|=0x0271;
USART1->CR1&=~(1<<12);
USART1->CR1&=~(1<<10);
USART1->CR1|=1<<3;
USART1->CR1|=1<<2;
USART1->CR1|=1<<5;
USART1->CR1|=1<<13;
USART1_Init_NVIC();
}
void USART1_IRQHandler()
{
unsigned int num;
num=USART1->DR;
USART1_PUT(num);
switch(num)
{
case 49:
LED0=!LED0;
printf("\r\n红灯翻转成功\r\n");
menu();
break;
case 50:
LED1=!LED1;
printf("\r\n绿灯翻转成功\r\n");
menu();
break;
case 51:
BEEP=!BEEP;
printf("\r\n蜂鸣器翻转成功\r\n");
menu();
break;
}
}
2、usart.h
#ifndef __USART_H
#define __USART_H
void USART1_Init(void);
void USART1_Init_NVIC(void);
void USART1_PUT(unsigned char C);
void menu(void);
#endif
3、led.c
#include "stm32f10x.h"
#include "led.h"
void LED_Init()
{
RCC->APB2ENR|=1<<3;
RCC->APB2ENR|=1<<6;
GPIOB->CRL&=0xFF0FFFFF;
GPIOB->CRL|=0x00200000;
GPIOB->ODR|=1<<5;
GPIOE->CRL&=0xFF0FFFFF;
GPIOE->CRL|=0x00200000;
GPIOE->ODR|=1<<5;
GPIOB->CRH&=0xFFFFFFF0;
GPIOB->CRH|=0x00000002;
GPIOB->ODR&=~(1<<8);
}
4、led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+12)
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)
#define GPIOA_IDR_Addr (GPIOA_BASE+8)
#define GPIOB_IDR_Addr (GPIOB_BASE+8)
#define GPIOC_IDR_Addr (GPIOC_BASE+8)
#define GPIOD_IDR_Addr (GPIOD_BASE+8)
#define GPIOE_IDR_Addr (GPIOE_BASE+8)
#define GPIOF_IDR_Addr (GPIOF_BASE+8)
#define GPIOG_IDR_Addr (GPIOG_BASE+8)
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n)
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n)
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n)
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n)
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n)
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n)
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n)
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n)
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n)
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n)
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n)
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n)
#define LED0 PBout(5)
#define LED1 PEout(5)
#define BEEP PBout(8)
void LED_Init(void);
#endif
5、delay.c
#include "stm32f10x.h"
#include "delay.h"
void System_clock(unsigned char PLL)
{
unsigned int Clock_OK;
RCC->CR|=1<<16;
while(!(RCC->CR&(1<<17)));
RCC->CFGR|=4<<8;
FLASH->ACR|=0x2;
RCC->CFGR|=1<<16;
PLL=PLL-2;
RCC->CFGR|=PLL<<18;
RCC->CR|=1<<24;
while(!(RCC->CR&(1<<25)));
RCC->CFGR|=2<<0;
do
{
Clock_OK=RCC->CFGR&0x0c;
}
while(Clock_OK!=0x08);
}
void SysTick_us(unsigned int time)
{
unsigned long num;
SysTick->VAL=0;
SysTick->LOAD=9*time;
SysTick->CTRL|=1<<0;
do
{
num=SysTick->CTRL;
}
while((num&0x01)&&!(num&(1<<16)));
SysTick->CTRL&=~(1<<0);
SysTick->VAL=0;
}
void SysTick_ms(unsigned int time)
{
unsigned long num;
SysTick->VAL=0;
SysTick->LOAD=9000*time;
SysTick->CTRL|=1<<0;
do
{
num=SysTick->CTRL;
}
while((num&0x01)&&!(num&(1<<16)));
SysTick->CTRL&=~(1<<0);
SysTick->VAL=0;
}
6、delay.h
#ifndef __DELAY_H
#define __DELAY_H
void System_clock(unsigned char PLL);
void SysTick_us(unsigned int time);
void SysTick_ms(unsigned int time);
#endif
7、main.c
#include "stdio.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
int main(void)
{
System_clock(9);
LED_Init();
USART1_Init();
menu();
while(1)
{
SysTick_ms(500);
}
}
六、效果展示
先看一下我们需要用的软件,叫XCOM,页面如上所示,串口选择里设置好我们编辑代码时所设置的串口参数,之后就可以用来查看串口发送的数据,也可以向串口发送数据。 程序下载成功,会出现如图的菜单,要求我们选择一个选项 我们在消息发送区输入1,点击发送,可以看到消息接收区,弹出了1,并紧跟着显示红灯翻转成功,大家可以观察自己的开发板,红灯会发生翻转,输入2绿灯翻转,输入3蜂鸣器翻转。 由于蜂鸣器不好展示效果,所以大家可以自行尝试。
7、总结
此文章只配置了USART1,关于其他两个,USART2和USART3,大家可以自己配置一下,但是要注意波特率的计算,这两个串口在低速总线上,大家要万分注意,还有就是对于GPIO口复用配置,此文代码中,led.c,delay.c,前面几篇有详细讲解,不会的可以查阅。
|