前言
由于之前作者仅仅只学习了51单片机的一些操作,对stm32单片机操作完全不会,过程中很曲折,如果有什么错的地方,希望可以告诉作者加以改正。
一、怎么点亮一个LED?
这个问题困扰了很久,因为stm32与51不同,51单片机直接可以操作引脚,而stm32要复杂得多。下面我们先来了解一下输入输出管脚——GPIO。
1、GPIO简介
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。 STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如 GPIOA、GPIOB、GPIOC,所有的 GPIO 引脚都有基本的输入输出功能。 (这里我们可以类比51单片机的引脚,点灯的时候只需要将GPIO管脚置为高电平或者低电平) 问题又来了,怎么设置GPIO管脚呢? 这个时候就会用到寄存器啦。
2、寄存器
1、片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1挂载低速外设,APB2 和 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中 APB1 总线的地址最低,片上外设从这里开始,也叫外设基地址。
总线名称 | 总线基地址 | 相对外设基地址的偏移 |
---|
APB1 | 0x4000 0000 | 0x0 | APB2 | 0x4001 0000 | 0x0001 0000 | AHB | 0x4001 8000 | 0x0001 8000 |
(到这里可以发现一个公式:地址=基地址+相对地址偏移) 2、外设基地址 GPIO 属于高速的外设 ,挂载到APB2 总线上。
外设名称 | 外设基地址 | 相对 APB2 总线的地址偏移 |
---|
GPIOA | 0x4001 0800 | 0x0000 0800 | GPIOB | 0x4001 0C00 | 0x0000 0C00 | GPIOC | 0x4001 1000 | 0x0000 1000 |
现在我们知道了GPIO的地址,是不是意味着可以直接通过地址来设置GPIO呢? 答案还是否定的,还需要操作里面特定功能的寄存器来设置GPIO管脚,比如:输入数据寄存器 GPIOx_IDR,GPIO 模式GPIOx_CRL或者GPIOx_CRH,时钟使能RCC_APB2ENR。
3、 APB2 外设时钟使能寄存器(RCC_APB2ENR) 为减小stm32的功耗而设置,如果不把对应管角的时钟使能,对应管脚不会工作。
这里我们以打开GPIOA的时钟为例子: 首先找到APB2的地址0x4001 0000,通过地址=基地址+相对地址偏移,得到RCC_APB2ENR的地址0x4001 0018。我们可以直接赋值为0x00000004
4、GPIO 模式GPIOx_CRL或者GPIOx_CRH 每个 GPI/O 端口有两个 32 位配置寄存器(GPIOx_CRL,GPIOx_CRH)。低8位GPIOx0—7在GPIOx_CRL,高8位GPIOx8—15在GPIOx_CRH。
什么是推挽输出?什么又是开漏输出呢? 推挽输出:可以输出高,低电平,连接数字器件。(我们点亮LED的模式) 开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
例如把GPIOB8设置为推挽输出,就找到GPIOB_CRH的地址,赋值为0x00000002。
5.输入数据寄存器 GPIOx_IDR 假如我们想点亮PA0,只需要将IDR0位置为0;
二、硬件
现在我们知道了软件的编写,硬件又该怎么连接呢? 我用的是stm32f103c8t6芯片,本次运用了GPIO_A8和GPIO_B8,和GPIO_C13,找到与之对应的管脚A8,B8,C13,用线把管脚引出来,再将LED的低电平引脚相连(LED长的引脚为高电平)。 采用串口通信,需要usb与STM32如图链接。其中要求usb的RXD连接核心板的TXD(A9),usb的TXD连接核心板的RXD(A10)。
烧录软件: 需要用到MCUISP 使用教程: STM32最小系统下载程序方法.
三、用寄存器点亮流水灯
代码如下:
#include<stdio.h>
#define pRCC_APB2ENR *((unsigned volatile int*)0x40021018)
#define pGPIOB_CRH *((unsigned volatile int*)0x40010c04)
#define pGPIOB_ODR *((unsigned volatile int*)0x40010c0c)
#define pGPIOC_CRH *((unsigned volatile int*)0x40011004)
#define pGPIOC_ODR *((unsigned volatile int*)0x4001100c)
#define pGPIOA_CRH *((unsigned volatile int*)0x40010804)
#define pGPIOA_ODR *((unsigned volatile int*)0x4001080c)
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
int main(void)
{
pRCC_APB2ENR =0x0000001c;
pGPIOB_CRH =0x00000002;
pGPIOC_CRH =0x00200000;
pGPIOA_CRH =0x00000002;
while(1)
{
pGPIOB_ODR =0x00000000;
Delay_ms(10000000);
pGPIOB_ODR =0x00000100;
Delay_ms(10000000);
pGPIOA_ODR =0x00000000;
Delay_ms(10000000);
pGPIOA_ODR =0x00000100;
Delay_ms(10000000);
pGPIOC_ODR =0x00000000;
Delay_ms(10000000);
pGPIOC_ODR =0x00002000;
Delay_ms(10000000);
}
}
实验效果
总结
通过本次的学习,初步了解并实现了从软件到硬件的过渡,同时也学习到了很多关于stm32寄存器的知识,在学习的过程中有很多次都想放弃了,特别是在写软件的时候。最后,功夫不负有心人,做出来的效果还是不错的。
|