第5章 工程新建
1.建立一个文件夹为Template
2.新建以下文件夹,拷贝相应的文件
根文件夹 |
子文件夹 |
程序文件 |
说明 |
来源 |
USER 用户文件夹 |
|
stm32f10x.h |
系统寄存器封装 |
STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\ CM3\DeviceSupport\ST\STM32F10x |
system_stm32f10x |
时钟 |
|||
system_stm32f10x |
||||
main.c |
|
STM32F10x_StdPeriph_Lib_V3.5.0\Project\ STM32F10x_StdPeriph_Template |
||
stm32f10x_it.c |
中断 |
|||
stm32f10x_it.h |
||||
stm32f10x_conf.h |
|
|||
HARDWARE 硬件外设 |
LED |
led.c |
|
|
led.h |
||||
KEY |
key.c |
|
|
|
key.h |
||||
SYSTEM 系统文件 |
delay |
delay.c |
|
|
delay.h |
||||
sys |
sys.c |
|
||
sys.h |
||||
usart |
usart.c |
|
||
usart.h |
||||
FWLIB 库文件 |
inc |
stm32f10x_gpio.h |
|
STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\ STM32F10x_StdPeriph_Driver |
src |
stm32f10x_gpio.c |
|||
CORE 内核文件 |
|
core_cm3.c |
|
STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\ CM3\CoreSupport |
core_cm3.h |
|
|||
startup_stm32f10x_hd.s |
|
STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\ CM3\DeviceSupport\ST\STM32F10x\startup\arm |
||
startup_stm32f10x_md.s |
|
|||
OBJ 编译文件 |
|
|
|
|
3.点击 Keil的菜单:Project –>New Uvision Project 新建工程到USER下面。
4.右键点击Target1,选择Manage Components
Project Targets一栏,我们将Target名字修改为Template,然后建立以下Groups,并添加文件,然后点击 OK。
5.然后在Project –>Options –>C/C++ 中做以下修改
“STM32F10X_HD,USE_STDPERIPH_DRIVER”到 Define输入框里面。
然后点击 Include Paths添加路径:
再选择“Output”选项,勾上下三个选项。 其中Create HEX file 是编译生成 hex 文件,
Browser Information 是可以查看变量和函数定义。还有就是我们要选择生产的hex文件
和项目中间文件放在哪个目录,点击“Select folder for Objects…”定位目录,我们的选
择定位到上面建立的OBJ 目录下面。
5.程序的下载
JLINK V8 支持JTAG和 SWD
打开魔术棒选项Options for Target,在在 Debug 栏做如下设置:
勾选了 Run to main(),该选项选中后,只要点击仿真就会直接运行到 main 函数,
如果没选择这个选项,则会先执行 startup_stm32f10x_hd.s 文件的 Reset_Handler,
再跳到main函数。
再点击Settings,设置J-LINK的一些参数:
再点击Utilities选项卡里面设置下载时的目标编程器。
点击 Settings修改配置选项。
点击仿真按钮 ,开始仿真。点击下载按钮,可以下载代码。
特别注意:开发板上的 B0 和 B1 都要设置到 GND,否则代码下载后不会自动运行的!
第6章 跑马灯
程序的结构
main.c文件包含main主函数,main.c文件包含led.h,该文件声明led.c中的函数。
-
{
-
delay_init(); //延时函数初始化
-
LED_Init(); //初始化与LED连接的硬件接口
-
while(1)
-
{
-
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
-
GPIO_SetBits(GPIOE,GPIO_Pin_5);
-
delay_ms(300);
-
GPIO_SetBits(GPIOB,GPIO_Pin_5);
-
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
-
delay_ms(300);
-
}
-
}
-
void LED_Init(void)
-
{
-
-
GPIO_InitTypeDef GPIO_InitStructure;
-
-
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
-
-
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
-
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
-
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
-
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5
-
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
-
-
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出
-
GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHz
-
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
-
}
STM32 的每个IO端口都有 7个寄存器来控制。他们分别是:
2个 32位的端口配置寄存器CRL和CRH;
2个 32位的数据寄存器IDR 和ODR;
1 个32位的置位/复位寄存器BSRR;
1个16位的复位寄存器 BRR;
1个 32位的锁存寄存器LCKR。
CRL和CRH 控制着每个 IO 口的模式及输出速率。
IDR是一个端口输入寄存器,该寄存器为只读寄存器,并且只能以16位的形式读出。要想知道某个 IO 口的电平状态,你只要读这个寄存器,再看某个位的状态就可以了。
在固件库中操作IDR 寄存器读取IO 端口数据是通过 GPIO_ReadInputDataBit函数实现的:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
比如我要读GPIOA.5 的电平状态,那么方法是:
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);
返回值是1(Bit_SET)或者 0(Bit_RESET);
ODR 是一个端口输出数据寄存器,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口的输出电平。
在固件库中设置 ODR 寄存器的值来控制 IO 口的输出状态是通过函数 GPIO_Write 来实现的:
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); //该函数一般用来往一次性一个GPIO的多个端口设值。
BSRR 寄存器是端口位设置/清除寄存器。该寄存器和 ODR 寄存器具有类似的作用,都可以用来设置GPIO 端口的输出位是 1还是0。该寄存器往相应位写0是无影响的,所以我们要设置某些位,我们不用管其他位的值。
例如你要设置 GPIOA 的第 1 个端口值为1,那么你只需要往寄存器 BSRR 的低16位对应位写 1即可:
GPIOA->BSRR=1<<1;
如果你要设置GPIOA 的第 1个端口值为0,你只需要往寄存器高 16位对应为写 1即可:
GPIOA->BSRR=1<<(16+1) ;
BRR 寄存器是端口位清除寄存器。该寄存器的作用跟 BSRR 的高 16 位雷同。
在STM32 固件库中,通过 BSRR和 BRR寄存器设置GPIO 端口输出是通过函数 GPIO_SetBits()和函数GPIO_ResetBits()来完成的。
-
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
-
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
在多数情况下,我们都是采用这两个函数来设置 GPIO 端口的输入和输出状态。比如我们要设置GPIOB.5 输出1,那么方法为:
-
GPIO_SetBits(GPIOB, GPIO_Pin_5);
反之如果要设置GPIOB.5输出位0,方法为:
-
GPIO_ResetBits (GPIOB, GPIO_Pin_5);
总结 通过库函数操作IO口的步骤:
1) 使能IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd();
2) 初始化IO 参数。调用函数 GPIO_Init();
3) 操作IO。操作 IO的方法就是上面我们讲解的方法。
使用库函数操作
int main(void) { delay_init(); //延时函数初始化 LED_Init(); //初始化与LED连接的硬件 while(1) { LED0=0; LED1=1; delay_ms(300); //延时300ms LED0=1; LED1=0; delay_ms(300); //延时300ms } }
在#include "led.h"中,定义了LED0和LED1:
#define LED0 PBout(5)// PB5 #define LED1 PEout(5)// PE5
而定义在SYSTEM文件夹的#include "sys.h"中
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
所以寄存器和库操作的的不同之处在于:
-
#define LED0 PBout(5)// DS0
-
#define LED1 PEout(5)// DS1
-
GPIO_SetBits(GPIOB, GPIO_Pin_5); //设置GPIOB.5输出 1,等同LED0=1;
-
GPIO_ResetBits (GPIOB, GPIO_Pin_5); //设置 GPIOB.5输出 0,等同 LED0=0;
第7张 蜂鸣器实验
#include "sys.h" #include "delay.h" #include "led.h" #include "beep.h" //ALIENTEK战舰STM32开发板实验2 //蜂鸣器实验 //技术支持:www.openedv.com //广州市星翼电子科技有限公司 int main(void) { delay_init(); //延时函数初始化 LED_Init(); //初始化与LED连接的硬件接口 BEEP_Init(); //初始化蜂鸣器端口 while(1) { LED0=0; BEEP=0; delay_ms(300); //延时300ms LED0=1; BEEP=1; delay_ms(300);//延时300ms } }
其中BEEP_Init()为初始化蜂鸣器端口,在BEEP.C中
void BEEP_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //BEEP-->PB.8 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); //根据参数初始化GPIOB.8 GPIO_ResetBits(GPIOB,GPIO_Pin_8); //输出0,关闭蜂鸣器输出 }
BEEP.H对以上初始化函数做了声明
#ifndef __BEEP_H #define __BEEP_H #include "sys.h" //蜂鸣器端口定义 #define BEEP PBout(8) // BEEP,蜂鸣器接口 void BEEP_Init(void); //初始化 #endif
最后还要修改Manage Components(管理组件)以及工程Options中文件位置。
第8章 按键输入
int main(void) { u8 t; delay_init(); //延时函数初始化 LED_Init(); //LED端口初始化 KEY_Init(); //初始化与按键连接的硬件接口 BEEP_Init(); //初始化蜂鸣器端口 LED0=0; //先点亮红灯 while(1) { t=KEY_Scan(0); //得到键值 if(t) { switch(t) { case KEY_UP: //控制蜂鸣器 BEEP=!BEEP; break; case KEY_LEFT: //控制LED0翻转 LED0=!LED0; break; case KEY_DOWN: //控制LED1翻转 LED1=!LED1; break; case KEY_RIGHT: //同时控制LED0,LED1翻转 LED0=!LED0; LED1=!LED1; break; } }else delay_ms(10); } }
1.首先看 KEY_Init(); //初始化与按键连接的硬件接口
//按键初始化函数 void KEY_Init(void) //IO初始化 { GPIO_InitTypeDef GPIO_InitStructure; //初始化KEY0-->GPIOA.13,KEY1-->GPIOA.15 上拉输入 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//PE2~4 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4 //初始化 WK_UP-->GPIOA.0 下拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0 }
2.key.h对key.c中的函数做了声明
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0 #define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1 #define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)//读取按键2 #define KEY3 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) #define KEY_UP 4 #define KEY_LEFT 3 #define KEY_DOWN 2 #define KEY_RIGHT 1 void KEY_Init(void); //IO初始化 u8 KEY_Scan(u8); //按键扫描函数 #endif
3.按键处理函数,输入参数为mode,告诉函数是否支持连续按。
u8 KEY_Scan(u8 mode) { static u8 key_up=1;//按键按松开标志 if(mode)key_up=1; //支持连按 if(key_up&&(KEY0==0||KEY1==0||KEY2==0||KEY3==1)) { delay_ms(10);//去抖动 key_up=0; if(KEY0==0)return KEY_RIGHT; else if(KEY1==0)return KEY_DOWN; else if(KEY2==0)return KEY_LEFT; else if(KEY3==1)return KEY_UP; }else if(KEY0==1&&KEY1==1&&KEY2==1&&KEY3==0)key_up=1; return 0;// 无按键按下 }
4.初始化完成之后,执行主函数
while(1) { t=KEY_Scan(0); //得到键值 if(t) //返回0不做处理 { switch(t) { case KEY_UP: //控制蜂鸣器 BEEP=!BEEP; break; case KEY_LEFT: //控制LED0翻转 LED0=!LED0; break; case KEY_DOWN: //控制LED1翻转 LED1=!LED1; break; case KEY_RIGHT: //同时控制LED0,LED1翻转 LED0=!LED0; LED1=!LED1; break; } }else delay_ms(10); //返回0的话,延时10ms
第九章 串口实验
串口设置的一般步骤可以总结为如下几个步骤:
1) 串口时钟使能,GPIO时钟使能
2) 串口复位
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 使能串口
7) 编写中断处理函数
与串口基本配置直接相关的几个固件库函数。这些函数和定义主要分布在stm32f10x_usart.h 和stm32f10x_usart.c 文件中。
1.串口时钟使能。串口是挂载在 APB2下面的外设,所以使能函数为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
2.串口复位。当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。
一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。复位的是在函数 USART_DeInit()中完成:
void USART_DeInit(USART_TypeDef* USARTx);//串口复位
比如我们要复位串口1,方法为:
USART_DeInit(USART1); //复位串口1
3.串口参数初始化。串口初始化是通过USART_Init()函数实现的,
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
这个函数的的第一个入口参数是指定初始化的串口标号,这里选择 USART1。
第二个入口参数是一个USART_InitTypeDef类型的结构体指针,这个结构体指针的成员变量用
来设置串口的一些参数。一般的实现格式为:
USART_InitStructure.USART_BaudRate = bound; //一般设置为 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
从上面的初始化格式可以看出初始化需要设置的参数为:波特率,字长,停止位,奇偶校验位,
硬件数据流控制,模式(收,发)。我们可以根据需要设置这些参数。
4.数据发送与接收。STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。
当向该寄存器写数据的时候,串口就会自动发送,当收到收据的时候,也是存在该寄存器内。
STM32 库函数操作USART_DR寄存器发送数据的函数是:
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
通过该函数向串口寄存器USART_DR写入一个数据。
STM32 库函数操作USART_DR寄存器读取串口接收到的数据的函数是:
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
通过该函数可以读取串口接受到的数据。
5.串口状态。串口的状态可以通过状态寄存器USART_SR读取。USART_SR的各位描述如
这里我们关注一下两个位,第 5、6位 RXNE 和TC。
RXNE(读数据寄存器非空),当该位被置 1的时候,就是提示已经有数据被接收到了,并且可以读出来了。
这时候我们要做的就是尽快去读取 USART_DR,通过读USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
TC(发送完成),当该位被置位的时候,表示USART_DR 内的数据已经被发送完成了。
如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:
1)读 USART_SR,写USART_DR。
2)直接向该位写 0。
状态寄存器的其他位我们这里就不做过多讲解,大家需要可以查看中文参考手册。
在我们固件库函数里面,读取串口状态的函数是:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE(读数据寄存器非空)以及 TC(发送完成)。
例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
我们要判断发送是否完成(TC),操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_TC);
6.串口使能。串口使能是通过函数USART_Cmd()来实现的,这个很容易理解,使用方法 是:
USART_Cmd(USART1, ENABLE); //使能串口
7.开启串口响应中断。有些时候当我们还需要开启串口中断,那么我们还需要使能串口中断,使能串口中断的函数是:
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类型有很多种。
比如在接收到数据的时候(RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断,接收到数据中断
我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:
USART_ITConfig(USART1,USART_IT_TC,ENABLE);
8.获取相应中断状态。当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄存器中的某个标志位。
经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是:
USART_GetITStatus(USART1, USART_IT_TC)
返回值是SET,说明是串口发送完成中断发生。
接下来我们分析代码main.c
#include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "usart.h" //ALIENTEK战舰STM32开发板实验4 //串口实验 //技术支持:www.openedv.com //广州市星翼电子科技有限公司 int main(void) { u8 t; u8 len; u16 times=0; delay_init(); //延时函数初始化 NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init(9600); //串口初始化为9600 LED_Init(); //LED端口初始化 KEY_Init(); //初始化与按键连接的硬件接口 while(1) { if(USART_RX_STA&0x8000) //1000 0000 0000 0000 第15位 接收完成标志
{ len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度 printf("\r\n您发送的消息为:\r\n\r\n"); for(t=0;t<len;t++) { USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束 } printf("\r\n\r\n");//插入换行 USART_RX_STA=0; }
else { times++; if(times%5000==0) { printf("\r\n战舰STM32开发板 串口实验\r\n"); printf("正点原子@ALIENTEK\r\n\r\n"); } if(times%200==0)printf("请输入数据,以回车键结束\n"); if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行. delay_ms(10); } } }
其中#include "usart.h"的内容如下:
#ifndef __USART_H #define __USART_H #include "stdio.h" #include "sys.h" #define USART_REC_LEN 200 //定义最大接收字节数 200 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 extern u16 USART_RX_STA; //接收状态标记 //如果想串口中断接收,请不要注释以下宏定义 void uart_init(u32 bound); #endif
接着是NVIC_Configuration()函数,该函数是设置中断分组号为2,也就是2位抢占优先级和 2位子优先级。
main.c的开始包含了sys.h头文件,该头文件声明了 void NVIC_Configuration(void) 函数,该函数在sys.c文件中
void NVIC_Configuration(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 }
接着是 uart_init(9600) 函数,该函数在uart.c中,由urat.h声明。
//初始化IO 串口1 //bound:波特率 void uart_init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
//①串口时钟使能,GPIO时钟使能,复用时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟 //②串口复位
USART_DeInit(USART1); //复位串口1 //USART1_TX PA.9 //③GPIO 端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9 //USART1_RX PA.10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10 //USART 初始化设置 //④串口参数初始化
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口 #if EN_USART1_RX //如果使能了接收 //Usart1 NVIC 配置 //⑤初始化NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//开启中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断 #endif //⑥使能串口
USART_Cmd(USART1, ENABLE); //使能串口 }
① 串口时钟使能,GPIO时钟使能
② 串口复位
③ GPIO 端口模式设置
④ 串口参数初始化
⑤ 初始化NVIC 并且开启中断
⑥ 使能串口
继续分析main函数
USART_SendData(USART1, USART_RX_BUF[t]); //向串口1 发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
第一句,其实就是发送一个字节到串口。第二句呢,就是我们在我们发送一个数据到串口之后,要检测这个数据是否已经被发送完成了。
USART_FLAG_TC 是宏定义的数据发送完成标识符。
在中断中,需要查询中断服务状态,要用到以下4个函数
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
做一个简单的串口收发程序:
1.修改串口1中断服务程序
void USART1_IRQHandler(void) //串口1中断服务程序 { u8 Res; #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了. OSIntEnter(); #endif if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据 USART_SendData(USART1, Res); //向串口1发送数据 } #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了. OSIntExit(); #endif } #endif
main函数修改while(1)为死循环即可,等待串口中断发生。