开发步骤
(1)使能串口时钟及 GPIO 端口时钟 前面说过 STM32F103ZET6 芯片具有 5 个串口,对应不同的引脚,串口 1 挂接 在 APB2 总线上,串口 2-串口 5 挂接在 APB1 总线上,根据自己所用串口使能总 线时钟和端口时钟。例如使用 USART1,其挂接在 APB2 总线上,并且 USART1 对 应 STM32F103ZET6 芯片管脚的 PA9 和 PA10,因此使能时钟函数如下: 1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能 GPIOA 时钟 2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能 USART1 时
(2)GPIO 端口模式设置,设置串口对应的引脚为复用功能 因为使用引脚的串口功能,所以在配置 GPIO 时要将设置为复用功能,这里 把串口的 Tx 引脚配置为复用推挽输出, Rx 引脚为浮空输入,数据完全由外部 输入决定。如下: 1. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出 PA9 2. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 3. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出 4. GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入 IO * 5. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入 PA10 6. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入 7. GPIO_Init(GPIOA,&GPIO_InitStructure);
(3)初始化串口参数,包含波特率、字长、奇偶校验等参数 要使用串口功能,必须对串口通信相关参数初始化,其库函数如下: 1. void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); 想必不用说,大家也知道第一个参数是什么意思,它是用来选择串口。第二 个参数是一个结构体指针变量,结构体类型是 USART_InitTypeDef,其内包含了 串口初始化的成员变量。下面我们就来看下这个结构体: 1. typedef struct 2. { 3. uint32_t USART_BaudRate; //波特率 4. uint16_t USART_WordLength; //字长 5. uint16_t USART_StopBits; //停止位 6. uint16_t USART_Parity; //校验位 7. uint16_t USART_Mode; //USART 模式 8. uint16_t USART_HardwareFlowControl; //硬件流控制 9. } USART_InitTypeDef;
(4)使能串口 配置好串口后,我们还需要使能它,使能串口库函数如下: 1. void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
(5)设置串口中断类型并使能 对串口中断类型和使能设置的函数如下: 1. void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalStat e NewState); 第一个参数用来选择串口,第二个参数用来选择串口中断类型,第三个参数 用来使能或者失能对应中断。由于串口中断类型比较多,所以使用哪种中断,我 们就需要对它进行配置。比如在接收到数据的时候(RXNE 读数据寄存器非空), 我们要产生中断,那么我们开启中断的方法是: USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断 又比如我们发送完数据时,要产生中断,可以配置如下: USART_ITConfig(USART1,USART_IT_TC, ENABLE)
(6)设置串口中断优先级,使能串口中断通道 在上一步我们已经使能了串口的接收中断,只要使用到中断,就必需对 NVIC 初始化,NVIC 初始化库函数是 NVIC_Init(),这个在前面讲解 STM32 中断时就已 经介绍过,不清楚的可以回过头看下
(7)编写串口中断服务函数 最后我们还需要编写一个串口中断服务函数,通过中断函数处理串口产生的 相关中断。串口中断服务函数名在 STM32F1 启动文件内就有,USART1 中断函数 名如下: USART1_IRQHandler 因为串口的中断类型有很多,所以进入中断后,我们需要在中断服务函数开 头处通过状态寄存器的值判断此次中断是哪种类型,然后做出相应的控制。库函 数中用来读取串口中断状态标志位的函数如下: 1. ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
软件编程
usart.h
#ifndef _usart_H
#define _usart_H
#include "system.h"
#define USART1_REC_LEN 200
extern u8 USART1_RX_BUF[USART1_REC_LEN];
extern u16 USART1_RX_STA;
void USART1_Init(u32 baud);
void USART1_IRQHandler();
#endif
usart.c
#include "usart.h"
u8 USART1_RX_BUF[USART1_REC_LEN];//数组长度
u16 USART1_RX_STA=0;//(标志位)15位接收完成标志,14接收到0x0d,0-13位接收有效字节
void USART1_Init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX发送引脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX接收引脚,不需要输出速度
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=baud;//波特率
USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;//校验位
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//无硬件流
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);//开启接收中断
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占式优先级,按自己需求配置
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;//响应式优先级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler()
{
u8 r=0;
//换行键共有两位,第一位是0x0d,第二位是0x0a;
//其实简单说就是为了怕数据丢失而存在一个数组中
if(USART_GetITStatus(USART1, USART_IT_RXNE)!=RESET)//接收中断函数
{
r=USART_ReceiveData(USART1);//接收数据,一位一位接受
if((USART1_RX_STA&0x8000)==0)
{
if(USART1_RX_STA&0x4000)//接收到0x0d
{
if(r!=0x0a)USART1_RX_STA=0;//如果没接收到0x0a,则重新开始
else USART1_RX_STA |=0x8000;//接收完成
}
else//没接收到0x0d
{
if(r==0x0d)USART1_RX_STA |=0x4000;//接收到0x0d
else
{
USART1_RX_BUF[USART1_RX_STA&0x3fff]=r;//后面14位来保存数据
USART1_RX_STA++;
if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;
//如果超出了长度还没接收到换行,则接收错误,直接清零
}
}
}
}
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "system.h"
#include "SysTick.h"
#include "beep.h"
#include "key.h"
#include "exti.h"
#include "time.h"
#include "pwm.h"
#include "usart.h"
int main()
{
u8 i=0;
u16 len=0;
u16 t=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置优先级分组
SysTick_Init(72);
LED_Init();
USART1_Init(115200);//波特率115200
while(1)
{
if(USART1_RX_STA&0x8000)//接收完成
{
len=USART1_RX_STA&0x3fff;//0-13位的数据长度
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART1_RX_BUF[t]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//TC是发送完成的标志
USART1_RX_STA=0;//为下一次接收清零
}
}
i++;
if(i%20==0)LED1=!LED1;
delay_ms(10);
}
}