STM32野火教程学习笔记
Posted 飞天大司马
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32野火教程学习笔记相关的知识,希望对你有一定的参考价值。
欢迎使用STM32
虽然经历了疫情期间的价格起飞,但是STM32系列的单片机仍然是各个控制领域内主流的微控制器。它是控制人的必修课之一。
STM32的编程方法
我们在学习51单片机的时候,通常是通过编写程序直接对其输入输出(i/o)口进行操作。而STM32单片机更加高级,它有更复杂的底层。因此我们对STM32单片机的编程方法有两种。一个是类似于51单片机的直接控制片内寄存器和i/o口的寄存器编程,一个是为了降低开发难度给开发者提供了巨大帮助的固件库编程。今天所学习的是更加底层的寄存器编程。尽管在开发的时候,我们用的基本上都是固件库编程,但是学习寄存器编程有利于我们了解STM32的底层,学一门技术只有掌握了牢固的底层才是打了一个好的基础。
前言
本人使用板子是野火STM32103指南者,型号是STM32VET6,兼容野火STM32f103霸道,型号是STM32ZET6。本笔记是在学习了野火官方教程的第11节课之后写下的。如何安装keilMDK,使用烧录器,如何新建工程模板,请观看野火stm32f103教程入门篇的前9节课。十分简单这里不再赘述。
用寄存器编程点亮第一个LED灯的地址定义
单片机的点灯是所有微控制器的入门标志,就好比C语言的Hello World!。这里我们类比一下51单片机的点灯方式。如图
可以看到我们直接对LED连接的P2.0口进行位操作拉低电平使其点亮。为何如此简单,奥妙就在于图中第一行REG52.H当中。这是一个C语言中叫做头文件的东西,它用来存放一些已经定义好的东西,比如在经典的stdio.h中有各种数学运算,REG52.H中就有定义每一个寄存器控制的i/o口也叫外设。
我们只要对这些定义好的东西操作,就可以通过单片机来控制各种电子器件了。
现在我们把目光转移到stm32上来,想要学会寄存器编程就要自己写头文件。先给头文件起个名,非常好起就叫它stm32f103.h好了。在里面我们开始写定义寄存器,从而方便我们控制STM32的i/o口即GPIO口(外设)的代码,这种方法叫寄存器映射。
//用来存放STM32寄存器映射的代码
// 外设 periphral
#define PERIPH_BASE ((unsigned int)0X40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0X10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0X20000)
#define RCC_BASE (AHBPERIPH_BASE + 0X1000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0X0C00)
既然要控制寄存器,那么我们要知道寄存器的在单片机内的地址,就如同警察叔叔找人要知道他的身份证号码知道他住哪一样。根据STM32官方给出的技术手册,外设的基地址(第一个地址,也叫起始位)是0x40000000,用C语言的unsigned int关键字将0x40000000转化为地址位(16进制数)。其名称根据外设的英语单词periphral定义为PERIPH_BASE。注意到这里我们还只是知道了寄存器的地址位还未对寄存器操作。
现在我们看第二行代码,“#define APB1PERIPH_BASE PERIPH_BASE” 它的意思是定义在APB1上面的外设基地址,就是整个单片机的外设的基地址:0x40000000。至于APB1是什么,野火教程前面的课程有讲解,简单点说就是一块放置外设的地方。STM32的外设就分别放在APB1和APB2上,因此第三行是定义APB2上的外设地址,它的地址在APB1基地址上增加0x10000(16进制数)。第四行定义AHB的基地址,AHB在之前的野火课程中也有讲解,这里简单说一下它是桥接控制单片机外设,时钟和单片机内核的部分,单片机想要工作必须打开它从而打开单片机的时钟。因此第四行代码“#define AHBPERIPH_BASE (PERIPH_BASE + 0X20000)”就是对AHB的地址定义了。
定义完外设地址以后,我们还不能着急去定义寄存器实现寄存器映射。还有两个重要的东西需要准备。第一个东西:时钟。单片机的时钟就好像一个人的心脏,时钟打开,单片机的心脏才开始跳动,所以时钟的地址就是AHB的初始地址上+0x1000。如第五行代码所示。第二个东西:我们要控制的GPIO口。野火stm32单片机的LED灯在B号GPIO上,所以我们要控制GPIOB。GPIO就属于单片机上的外设了,它的基地址就是APB2的基地址上+0x0C00。到这里点灯所有所需的地址位已经准备好了,我们要开始通过这些地址位访问内存来控制寄存器点灯了。
用寄存器编程点亮第一个LED灯的寄存器操作
代码如下
#define RCC_APB2ENR (unsigned int)(RCC_BASE + 0x18)//时钟使能
#define GPIOB_CRL (unsigned int)(GPIOB_BASE + 0x00)
#define GPIOB_CRH (unsigned int)(GPIOB_BASE + 0x04)
#define GPIOB_ODR (unsigned int)(GPIOB_BASE + 0x0C)
首先我们要打开时钟,那么就要启动时钟使能位,时钟使能寄存器我们把它的名字叫做RCC_APB2ENR,它的地址是时钟基地址上+0x18。
这里有一个难理解的点,我们为什么要进行一个指针强制类型转换这个操作?原因很简单,目前定义的那些地址例如APB1PERIPH_BASE,它还只是反映地址的立即数。打个比方,警察要找你,知道了你的身份证号。但是这不代表找到你了,只是知道了你的一个代号,要找到你还得去线下你的住址找你。这样的行为就像指针强制类型转换的作用了,把代表寄存器地址的立即数,强制转化位指针,这样它就是一个真正的地址了。真正的地址就可以访问寄存器来控制外设GPIO了。(指针不懂的去补补C语言指针,其实指针就是一个地址)
回到寄存器控制上来,我们配置完时钟后,下一步就是配置控制GPIO(外设)的寄存器。点灯操作我们需要至少控制3个。端口配置低寄存器(CRL)、端口配置高寄存器(CRH)、端口输出数据寄存器(ODR)最后3行就是这些寄存器的位和强制类型转换的操作。
用寄存器编程点亮第一个LED灯
C语言控制微控制器的程序基本上都是在main函数里的,因此我们在main函数里开始写如下代码
#include “stm32f10x.h”
int main(void)
//打开GPIOB的时钟
RCC_APB2ENR |=((1)<<3);
//清空控制PB0的端口位
GPIOB_CRL &= ~( (0x0F)<< (4*0));
// 配置IO口为输出
GPIOB_CRL |=((1)<<(4*0)
// 控制ODR寄存器
GPIOB_ODR &= ~(1<<0);
//GPIOB_ODR |= (1<<0);关闭
void SystemInit(void)
//函数体为空,目的是为了骗过编译器不报错
这里我们看到我们可以类似操作51单片机那样直接对外设地址操作从而打开时钟,拉低GPIO口的电平点亮LED灯了。最后面那个骗过编译器不报错是前面几节课的内容,打上就行,不用太深入了解,以后用不到。
我们发现stm32对寄存器的操作不像51单片机可以直接赋值0或1,而是采用C语言里特殊的|=和&=~,|=是用于置1某一位而不改变其他位(一个寄存器上有好几位,stm32单片机的一个寄存器有32位),比如这里的打开时钟是GPIOB的时钟。根据数据手册GPIOB的时钟在RCC_APB2ENR寄存器的第三位,把他第三位置1就单独打开了GPIOB的时钟,这里我把数据手册的截图给大家看一下。
另一个&=~是置0操作,置0寄存器某一位而不改变其他位例如主程序的第二行程序 GPIOB_CRL &= ~( (0x0F)<< (40));它的操作是将端口配置低寄存器初始4位全部置0,而不改变其他位。
第三行代码是控制GPIO口位为10MHz通用推挽输出模式。将1左移40位变成0001(此寄存器每4位控制一个GPIO位的输出模式)。
第四行就是将野火stm32指南者的LED绿灯点亮的操作,将ODR寄存器第1位置0(1左移0位)拉低电平。
主要我们就完成了点灯,入门成功!
野火教程B站链接
https://www.bilibili.com/video/BV1yW411Y7Gw/?p=6&spm_id_from=333.880.my_history.page.click&vd_source=16419a44923b86308e680f95ec76193a
以上是关于STM32野火教程学习笔记的主要内容,如果未能解决你的问题,请参考以下文章