目录
一、地址映射和寄存器映射原理
1、寄存器是什么?
寄存器是中央处理器内的组成部分。寄存器是有限存储容量的高速存储部件,它们可以用来暂存指令、数据和地址。简单的来说,寄存器就是一个存放指令、数据和地址的容器。
按照功能的不同,可以将寄存器分为基本寄存器和移位寄存器两大类。基本寄存器只能并行送入数据,也只能并行输出。以为寄存器中的数据可以在移位脉冲的作用下一次逐位右移或者左移,数据既可以并行输入、并行输出,也可以串行输入、穿行输出,还可以并行输入、串行输出,或串行输入、并行输出,十分灵活,用途也非常的广泛。
2、地址映射和寄存器映射原理
根据百度百科的介绍,地址映射为:
为了保证CPU执行指令时可正确访问存储单元,需将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址,这一过程称为地址映射。
寄存器映射:
在存储器上的区域单元中,每隔单元对应不同的功能,当我们控制这些单元就可以驱动外设工作。我们可以找到每隔单元的其实地址来访问,然后通过C语言的指针的操作方式来访问这些单元。但是这种方式很难记忆,而且十分的容易出错。所以,我们就可以根据每隔单元功能的不同,给内存单元取一个别名。
给以分配好的地址(通过存储器映射实现)的有特定功能的内存单元取别名的过程就叫寄存器映射。
二、GPIO端口的初始化
1、GPIO简介
根据百度百科的介绍:
GPIO(英语:General-purpose input/output),通用型之输入输出的简称,功能类似8051的P0—P3,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。
既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。
2、GPIO初始化
1、时钟配置
时钟控制名字叫做RCC,属于AHB总线。GPIOB属于APB2。
2、输入输出模式介绍(四输入四输出)
浮空输入:可读取引脚电平,若引脚悬空,则电平不稳定
上拉输入:可读取引脚电平,内部连接上拉电阻,悬空时默认为高电平
下拉输入:可读取引脚电平,内部连接下拉电阻,悬空时默认为低电平
模拟输入:GPIO无效,引脚直接接入内部ADC开漏输出:可输出引脚电平,高电平为高阻态,低电平接VSS
推挽输出:可输出引脚电平,高电平接VDD,低电平接VSS
复用开漏输出:由片上外设控制,高电平为高阻态,低电平接VSS
复用推挽输出:由片上外设控制,高电平接VDD,低电平接VSS
3、输入输出配置
当 I/O 端口配置为输入时:
- 输出缓冲器被禁止
- 施密特触发输入被激活
- 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接
- 出现在 I/O脚上的数据在每个 APB2 时钟被采样到输入数据寄存器
- 对输入数据寄存器的读访问可得到 I/O 状态
下图展示的是输入配置
当 I/O 端口被配置为输出时:
- 输出缓冲器被激活
开漏模式:输出寄存器上的 0 激活 N-MOS,而输出寄存器上的 1 将端口 置于高阻状态(P-MOS从不被激活)。
推挽模式:输出寄存器上的 0 激活 N-MOS,而输出寄存器上的 1 将激活 P-MOS。 - 施密特触发输入被激活
- 弱上拉和下拉电阻被禁止
- 出现在 I/O 脚上的数据在每个 APB2 时钟被采样到输入数据寄存器
- 在开漏模式时,对输入数据寄存器的读访问可得到 I/O 状态
- 在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。
下图展示了输出的配置
4.最大速率设置
5.初始化步骤:
①. 第一步:使能GPIOx口的时钟
②. 第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式
③. 第三步:调用GPIOx初始化函数进行初始化
④. 第四步:调用GPIO-SetBits函数,进行相应位的置位
三、实战
背景:假设你手中已有 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。
1、试验器材简介:
STM32F103C8T6是一款基于ARM Cortex-M 内核STM32系列的32位的微控制器,程序存储器容量是64KB,需要电压2V~3.6V,工作温度为-40°C ~ 85°C。
1. 寄存器起始地址表,查询RCC地址范围,控制的寄存器位于APB2中
2.外设时钟使能寄存器,设偏移量为0x18,起始地址0x4002 1000,则该寄存器地址为
0x4002 1000+0x18=0x4002 1018
3.使能对应端口时钟
根据上图,我们想要要开启B时钟,则应该将位3赋值为1,其余同理。演示将B时钟开启
#define RCC_APB2ENR *((unsigned volatile int*)0x40021018)
RCC_AP2ENR|=1<<2;
4.端口配置寄存器
确定使用的引脚:本次试验将使用GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚
端口配置低寄存器(CRL) 只有5号端口是属于端口配置低寄存器,偏移地址为0x00
而9和13端口都是属于端口配置高寄存器,偏移地址为0x04
5.找到GPIOx端口基地址
6.配置对应引脚寄存器,基地址+偏移量
因此,我们可以写出如下代码
//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
//----------------GPIOB配置寄存器 -----------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
//----------------GPIOC配置寄存器 -----------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
7.设置输出模式为推挽输出,输出速度为2Mhz
GPIOA_CRL&=0xFF0FFFFF; //位清零
GPIOA_CRL|=0x00200000; //PA5推挽输出,把第20, 21, 22, 23位变为0010
GPIOB_CRH&=0xFFFFFF0F; //位清零
GPIOB_CRH|=0x00000020; //PB9推挽输出,把第4, 5, 6, 7变为0010
GPIOC_CRH&=0xFF0FFFFF; //位清零
GPIOC_CRH|=0x00200000; //PC14推挽输出,把第20, 21, 22, 23变为0010
2、代码实现
//--------------APB2???????------------------------
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA????? ------------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
//----------------GPIOB????? ------------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ORD *((unsigned volatile int*)0x40010C0C)
//----------------GPIOC????? ------------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ORD *((unsigned volatile int*)0x4001100C)
//-------------------???????-----------------------
void Delay_wxc( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
//------------------------???--------------------------
int main()
{
int j=100;
RCC_AP2ENR|=1<<2; //APB2-GPIOA??????
RCC_AP2ENR|=1<<3; //APB2-GPIOB??????
RCC_AP2ENR|=1<<4; //APB2-GPIOC??????
//????????? RCC_APB2ENR|=1<<3|1<<4;
GPIOA_CRL&=0xFF0FFFFF; //??? ??
GPIOA_CRL|=0x00200000; //PA7????
GPIOA_ORD|=1<<5; //???????
GPIOB_CRH&=0xFFFFFF0F; //??? ??
GPIOB_CRH|=0x00000020; //PB9????
GPIOB_ORD|=1<<9; //???????
GPIOC_CRH&=0xF0FFFFFF; //??? ??
GPIOC_CRH|=0x02000000; //PC14????
GPIOC_ORD|=1<<14; //???????
while(j)
{
GPIOA_ORD=0x0<<0; //PB0???
Delay_wxc(1000000);
GPIOA_ORD=0x1<<0; //PB0???
Delay_wxc(1000000);
GPIOB_ORD=0x0<<9; //PB9???
Delay_wxc(1000000);
GPIOB_ORD=0x1<<9; //PB9???
Delay_wxc(1000000);
GPIOC_ORD=0x0<<14; //PC15???
Delay_wxc(1000000);
GPIOC_ORD=0x1<<14; //PC15???
Delay_wxc(1000000);
}
}
3、连接电路
4、烧录程序
我们需要一个软件来帮助我们进行烧录(也就是mcuisp)
点进去的界面如下图:并如图这样设置
当一切都准备就绪之后就可以进行烧录了
1、将电路上的USB接口插在电脑上,并将芯片上的boot0置1,boot1置0,可能看的不是很清楚,就是将芯片上的黄色小方块如图中所示
2、先点击 ‘读器件信息’,然后点击 ‘开始编程’,当USB串口那里的灯闪蓝灯了,就说明程序录入成功
3、然后将串口拔出来,将boot0和boot1都置0,然后再插进去,并点击开始编程,之后就可以看到LED灯开始在循环的闪烁了
四、安装stm32cubemx
1、安装软件
1、点击下载文件,再点击next进入下一个界面
2、同意协议,再点击next进入下一个界面
3、选择第一个即可,第二个是收集信息
4、确定安装位置,默认是在C盘中,路径最好不要有中文;同时,安装目录下也不要有其他的文件
5、接着会出现一个弹框,提示你将要创建文件夹
6、选择默认选项即可(Default),开始安装
7、安装成功
2、下载HAL库
1、打开cubemx,点击上方菜单栏的Help,再点击“管理库”
2、选择想要的下载的HAL库(我这里是已经下载过了)
3、创建项目
1、返回到刚进来时的界面,点击方框中的按钮
2、勾选信息
3、
4、配置时钟
5、选择引脚
6、进入Project Manager界面,填写项目名称,项目路径和IDE
7、创建成功,开始编写代码
8、编写主函数
替换内容:
SystemClock_Config(); //系统时钟初始化
MX_GPIO_Init(); //gpio初始化
while (1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); //PA5亮灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET); //PB9熄灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET); //PC14熄灯
HAL_Delay(1000); //延时1s
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); // PA5熄灯
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET); // PB9亮灯
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET); // PC14熄灯
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
HAL_Delay(1000);
}
9、烧录,过程与前面的一样,就不过多赘述
五、观察GPIO曲线图
1、设置:
2、进入调试界面
按debug按钮,或者快捷键Ctrl+F5
3、开启逻辑分析器
4、选择引脚,点击Setup
通过Zoom将Grid调整为1s,并勾选如图的两项
5、运行
结果如图所示:引脚为低电平的灯亮,高电平的灯不亮,高低电平转换周期(LED闪烁周期)为1s左右。
6、参考手册以及编程工具
STM32F103中文教程及参考手册
提取码:xxnf
mcuisp(烧录软件)
提取码:5273
Last、总结:
本文的作者为一个萌新,所以有错请大家指正。同时,本次的试验也让我这个小萌新更加的了解嵌入式系统开发的过程和相应的软件。