版权归如下公司,禁止非授权转载:
- 北京西普阳光教育科技股份有限公司(https://www.simpleware.com.cn)
- 维周机器人科技有限公司(http://www.vejoe.com)
文章目录
【实验目的】
1、通过练习初步掌握STM32基本IO口的使用,迈向STM32的第一步。
2、通过对GPIO端口配置和对FLASH的读写编程,设计程序按要求点亮小灯,初步了解和熟悉GPIO端口的配置方法以及FLASH的写入和读取。
【实验原理】
一、GPIO简介
STM32的IO口可以由软件通过编程配置成8种模式:输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽式复用功能和开漏复用功能。每个IO口可以自由编程,单IO口寄存器必须要按32位字被访问。STM32的每个IO端口都有7个寄存器来控制,如表1所示。
表1 GPIO相关寄存器
在STM32芯片上,I/O引脚可以被软件编程设置成各种不同的功能,如输入或输出,所以被称为GPIO(General-purposeI/O)。而GPIO引脚又被分为GPIOA、GPIOB…GPIOG不同的组,每组端口分为0~15,共16个不同的引脚,对于不同型号的芯片,端口的组和引脚的数量不同,具体请参考相应芯片型号datasheet。
二、STM32时钟系统
从时钟频率来说,分为高速时钟和低速时钟,高速时钟是提供给芯片主体的主时钟,而低速时钟只是提供给芯片中的RTC(实时时钟)及独立看门狗使用。
从芯片角度来说,时钟源分为内部时钟与外部时钟源,内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。而外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号。
我们实验中调用了RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState),其中RCC_APB2Periph用于指示要配置的时钟,在应用时可以使用“|”或操作同时配置几个外设的时钟;NewState用于设置状态,可输入“Disable”关闭或者“Enable”使能时钟。
三、STM32的内部FLASH读写
在STM32芯片内部有一个FLASH存储器,它主要用于存储代码,我们在电脑上编写好程序后,使用下载器把编译后的代码文件烧录到该内部FLASH中,由于FLASH存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部FLASH中加载代码并运行。
我们实验中实现对FLASH的读写是通过调用库函数:
STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
其中WriteAddr为起始地址(此地址必须为2的倍数),pBuffer为数据指针,NumToWrite和NumToRead为半字数(就是要写入或读取16位数据的个数)。
四、硬件连接和GPIO相关库函数介绍
控制LED灯,通过控制I/O引脚电平的高低来实现。通过图1,LED的硬件连接图可知当I/O引脚为高电平时,LED点亮,为低电平是,LED熄灭。
图1 LED硬件连接示意图
对应GPIO_InitTypeDef类型的结构体配置说明如下,其中GPIO 输入/输出模式配置如表2所示,GPIO的输出速率配置如表示所示。
typedef struct
{
u16 GPIO_Pin; //指定将要配置的GPIO引脚序号
GPIOSpeed_TypeDef GPIO_Speed; //指定GPIO引脚可以输出的最高频率
GPIOMode_TypeDef GPIO_Mode; //指定GPIO引脚配置的工作状态
}GPIO_InitTypeDef;
表2 GPIO 输入/输出模式配置
表3 GPIO 输出速率配置
对结构体中的各个成员进行赋值,然后把这个变量作为“GPIO初始化函数”GPIO_Init的输入参数,该函数能根据这个变量值中的内容去配置寄存器,从而实现GPIO的初始化。对初始化结构体赋值后,把它输入到GPIO初始化函数GPIO_Init,由它来实现寄存器配置,完成GPIO引脚初始化。
库函数GPIO_Init根据GPIO_InitStruct中指定的参数初始化外设GPIOx寄存器,调用形式为:
GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct)
其中GPIOx为GPIO_TypeDef类型的指针,指向GPIO端口的地址;GPIO_InitStruct为GPIO_TypeDef结构体指针,指向初始化变量。
我们实验中选择的GPIOC的1~3引脚连接到电路上的3个LED小灯。控制引脚的电平变化时,直接使用GPIO_SetBits、GPIO_ResetBits函数控制引脚的输出,就可以控制小灯的亮灭。
库函数GPIO_SetBits/GPIO_RessetBits的主要功能为置/复位指定的数据端口位,其调用形式为:
GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
其中GPIOx为GPIO_TypeDef类型的指针,指向GPIO端口的地址;GPIO_Pin为指定要写入的端口位,可输入宏GPIO_Pin_0-15,表示GPIOx端口的0-15号引脚。其他库函数说明见附录说明。
【实验环境】
操作系统
Windows7/8/10,32bit/64bit
硬件设备
双轮自平衡机器人。小车所搭载的电路板已经将STM32芯片上的PC1、PC2、PC3管脚引出,连接到电路上的3个LED灯上。
ST-Link下载器(包括USB线与下载线)。如图1.2所示。
图1.2 ST-Link下载器与下载线
软件环境:
Keil 5
实验场地:
无
实验步骤
1 配置工程环境
1.1 打开已经建立好的工程模板,在新建立的工程模板中已经添加五个文件夹,分别命名为USER、HARDWARE、SYSTEM、CORE、FWLib文件夹,如图3所示。其中USER文件夹存放的是主函数,HARDWARE文件夹存放的是本实验对应的硬件设备函数,SYSTEM存放的是本课程所有实验通用的函数,CORE文件夹存放的是启动文件,FWLib文件夹存放的是底层驱动函数。
图3 工程模板对应的文件夹
1.2 在SYSTEM文件夹下新建两个文件,分别为led.c和led.h。分别存放LED控制函数与LED头文件,如图4所示。以及在HARDWARE文件夹下添加FLASH相关的stmflash.c和stmflash.h文件,如图5所示。
图4 在SYSTEM文件夹下建立led.c与led.h文件
图1.5 在HARDWARE文件夹下添加stmflash.c和stmflash.h文件
2 开启时钟,完成端口初始化
2.1 打开程序中的led.c文件,首先将led.h文件包含进来。对LED_Init函数进行编写和修改。选择要控制的GPIO引脚并设置引脚模式和速率。
#include "led.h"//包含头文件
/*初始化PC1、PC2、PC3为输出口。并使能时钟*/
/*对LED的IO口进行初始化*/
void LED_Init(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启GPIOC的外设时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/*选择要控制的GPIOC引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIOC*/
GPIO_Init(GPIOC,&GPIO_InitStructure);
/*关闭所有led灯 */
GPIO_ResetBits(GPIOC,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3 );
}
2.2 打开led.h文件,添加LED_Init函数声明。
#ifndef __LED_H
#define __LED_H
void LED_Init(void); //初始化
#endif
2.3 打开stmflash.c文件,查看在编写程序时需调用的STMFLASH_Write,STMFLASH_Read函数的变量类型和用法。
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数),pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
编写main函数实现实验要求
函数流程图如图6所示,首先对定义函数所使用的的变量,并对各模块进行初始,循环写入和读取FLASH,并根据数据对LED小灯的闪烁次数进行控制。
图6 程序流程图
3 编写程序
3.1 将工程编译需要用到的头文件包含进来
#include "delay.h" //包含延时函数头文件
#include "stmflash.h" //包含FLASH头文件
#include "led.h" //包含LED头文件
3.2 编写要写入FLASH中的字符串数组
#define FLASH_SAVE_ADDR 0X08070000 //设置FLASH保存地址(必须为偶数)
#define ON 0 //定义全局变量ON=0
#define OFF 1 //定义全局变量OFF=1
3.3 在主函数中调用延时函数与LED初始化函数。
delay_init(); //延时函数初始化
LED_Init(); //调用LED初始化函数
3.4 将已编辑好的初始化函数写入main函数。
int main(void)
{
u16 j=0;
u16 datatemp;
u16 ledtime=5;
delay_init(); //延时函数初始化
LED_Init();
while(1)
{
......
......
}
}
3.5 因为要实现在FLASH中写入一个数字,再将这个数字读出作为小灯的闪烁次数,因此接下来需要编写一个循环来实现小灯的闪烁,这里我们使用while语句来进行循环。
3.6 在while(1)中编写实现读写FLASH的程序。首先调用STMFLASH_Write函数,写入FLASH存储起始地址、数据指针地址和写入数据的个数。之后调用STMFLASH_Read读取存储的数据。
STMFLASH_Write(FLASH_SAVE_ADDR,&ledtime,1);
STMFLASH_Read(FLASH_SAVE_ADDR,&datatemp,1);
3.7 在while(1)中编写小灯闪烁程序。定义变量j,当j小于FLASH中写入的数字时,满足if语句的判断条件,将GPIO_Pin_2管脚位置低,小灯点亮,变量j加1,延时100ms后关闭,再重新if判断,直到完成小灯的j次闪烁。
if(j<datatemp)
{
GPIO_ResetBits(GPIOC,GPIO_Pin_2);
delay_ms(100);
GPIO_SetBits(GPIOC,GPIO_Pin_2);
delay_ms(100);
j++;
}
4 编译并下载,观察实验现象
4.1本实验采用仿真器为STLink V2,将仿真器与小车相连,注意正负极不要接反,如图7所示。
图7 仿真器与下载线连接图
4.2编译程序,当编译完成后,如果没有问题,Build Output栏会出现无错误,无警号的提示,如图8所示。
图8 编译通过后Build Output栏提示信息
4.3下载程序到小车,并观察小灯闪烁。如图9所示。
图9 Keil编译环境下的下载按键
【实验思考】
一、选择题
题目1:下面哪条语句是设置引脚输出模式(D)
A:GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
B:GPIO_Init(GPIOC, &GPIO_InitStructure);
C:GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
D:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
题目2:GPIO所用的时钟PCLK2的默认值是多少?(A)
A:72MHz
B:36MHz
二、简答题
题目1:GPIO分别有哪些工作模式?简单介绍各个模式的工作场合。
GPIO引脚配置为上拉输入模式,在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为1,高电平。而下拉模式则相反,在默认状态下其引脚数据为0,低电平。浮空输入模式一般用于标准的通讯协议如I2C、USART的接收端。模拟输入模式可以用于ADC模块。
普通推挽输出模式一般应用在输出电平为0和3.3伏的场合。而普通开漏输出一般应用在电平不匹配的场合。对于相应的复用模式,则是根据GPIO的复用功能来选择的,如GPIO的引脚用作串口的输出,则使用复用推挽输出模式。如果用在IC、SMBUS这些需要线与功能的复用场合,就使用复用开漏模式。