1.创建工程
处理器执行程序的时候怎么执行
处理器执行程序都是先执行汇编程序,然后在汇编程序里面跳到主函数里面,所以要先写好汇编程序
不过这个一般官方提供了,只需把这个文件拷到我们的工程文件夹里面
接着将汇编文件放到我们的工程里面(添加已经存在的文件到这个组里面)
将_main 这个地址送到 R0 里面去,BX (汇编语言的跳转指令)跳转到R0里面去,刚才我们已经将函数的地址(函数的名字)放在R0里面去了(在C语言函数的名字代表一个地址),所以现在是将地址传到R0里面去,接下来跳转到地址执行它的东西
所以接下来要写一个main函数,等一下会调到main函数里面来
添加一个main.c文件
1.点击中文参考手册
2.点击时钟部分
芯片的时钟系统的作用 : 时钟系统提供的是脉冲,就像人的心脏一样,不停的跳动,往全身各个地方输送血液,让我们全身能够动起来
任意一个模块,比如 I2S3 ,如果想要时钟能够过来让它工作,怎么做,就是要先将 I2S3 前面的与门打开,这个与门怎么打开,就是要外设时钟使能端使能,把它设置为1,输入是什么输出就是什么,如果为使能端为0,信号过不来,这个 I2S3 就不能工作了
以上说明 : 我们STM32 每一个模块,在使用的时候,都要先去使能它前面这个与门的时钟
所以,要先使能 PEx 端口的时钟
PEx 在哪个地方使能呐?
看数据手册,找寄存器
找到PE组 端口的使能,现在我们要将 第六位 设置为1
在 C语言里面这个 RCC_APB2ENR 是一个标识符,要先定义才能使用,所以我们要先定义它,要它代表一个寄存器(寄存数据的电路,是一个存储器)
用什么定义 #define RCC_APB2ENR 要先找到APB2ENR的地址
数据手册
复位和时钟控制(RCC) 这个模块有很多寄存器,这个寄存器占据的存储空间,从0x4002 1000到0x4002 13FF 这么一块存储单元
所以我们知道RCC_APB2ENR 的基地址是0x4002 1000 就像一个班里面的学号第一位的同学,现在我们要找它的偏差,就是它的偏移量
这个 RCC模块 内部有很多寄存器,这些寄存器一个个排列,那现在排到 APB2ENR 的时候它的偏移地址是 0x18
RCC_APB2ENR = 初始地址 + 偏移量
#define RCC_APB2ENR (0x40021000 + 0x18) 所以我们现在得到APB2ENR 的地址是这个
这个地址在我们编译器看来不是一个地址,它只是一个整型数据,要想变为地址怎么变? 前面加一个 * 号 去修饰就变为一个地址了 #define RCC_APB2ENR (*) (0x40021000 + 0x18)
变为一个地址现在有一个问题,我们c 语言里面你要说明的这个地址对应的存储单元他里面存的是什么类型的数据,现在看手册已经知道这是一个寄存器,我们STM32的寄存器,每一个都是32位,而且它里面保存的都是无符号整型数据,所以现在要说明这个地址指向的存储单元里面存放的是无符号整型 #define RCC_APB2ENR (unsigned int*)(0x40021000 + 0x18) ,所以现在我们知道这是一个地址了,但是现在我们希望能够有这样的赋值操作 RCC_APB2ENR |= 1<<6;
就相当于 RCC_APB2ENR = 3 , 这是一个赋值的操作,赋值操作你把3,你不能赋给一个地址,只能赋值给一个存储单元
要想把一个地址变为存储单元,怎么变,在前面再加一个 * 号
#define RCC_APB2ENR (*(unsigned int*)(0x40021000 + 0x18)) 现在变为一个存储单元了
对寄存器是一种特殊的存储单元,怎么特殊,我们每次访问存储单元的时候,我们希望不是从缓存里面访问,而是直接访问这个寄存器 怎么直接访问 用volatile修饰
加上 volatile 修饰了以后,CPU去访问这个寄存器的时候,他就不会访问到缓存了,每次都是从0x40021000 + 0x18 这个地方拿数据,它会慢一些,但能够保证数据每次都是正确的,否则缓存那边没有来的及更新的话,它有可能出现错误
#define RCC_APB2ENR (*(volatile unsigned int*)(0x40021000 + 0x18))
现在对这个端口的时钟已经使能好了,接下里对这个模块进行操作,目标是将PE12 设置为低电平,如何设置为低电平?
看这2个寄存器
L 代表低位 H代表高位
意思是配置第 0 个引脚用这 4位去配置
意思是配置第 1 个引脚用这 4位去配置
所以最后发现低位寄存器配置的是第0到第7 (8个引脚)
发现高位寄存器配置的是第8到第15(7个引脚)
所以我们找到 12 引脚在这个位置 ,一共有4位,
我们对寄存器配置的话,先把它清零,然后再把数据写进去
现在要把这4 位清零
左移16位 ,这4位变成了 1111
然后取反,这 4 位就变成了 0000
GPIOE_CRH &= ~(0xf << 16); //然后 GPIOE_CRH = GPIOE_CRH & ~(0xf << 16);
0 & 0 = 0; 寄存器这4 位就被清零0了
现在进行设置,应该设置为?,这4个位有16 种状态,下面3种都是输出,对LED无论哪种速度都可以,这些速度是为了匹配外部的高速电路,
配置输入模式是上面这4个,配置为输出模式是下面的这4个
00 通用推挽输出模式的特点 :这种引脚模式可以输出 0 ,也可以输出1
01 通用开漏输出模式的特点 : 这种引脚只能输出 0,输出1 的话需要特殊处理
复用 : 就是重复利用 ,比如把这个引脚复用为串口的发送或者接收引脚,而不是输入输出
我们希望 0 和 1 都可以用,就用 00 这种方式
将 CNF 设置为 00 MODE 设置为 01
最后这4位就变成了 00 01 最后就是 0x01
GPIOE_CRH |= 1 << 16;
GPIOE_CRH 的偏移地址:
基地址: 可以看到E 口有很多寄存器,占据存储单元 0x4001 1800 - 0x4001 1BFF
最后地址为 : #define GPIOE_CRH (*(volatile unsigned int*)(0x40011800 + 0x04))
接下来输出0 或者1
O 代表输出 D 代表数据 合起来就是端口输出数据寄存器
一共16位,刚好控制的就是 16 个引脚的输出
如果下面这里是GPIOE_ODR,那么就是控制的是GPIOE 16个引脚的输出
如果这个第12 位为1,那么这个第12引脚就输出高电平,如果这个第12 位为0,那么这个第12引脚就输出低电平
GPIOE_ODR &= ~(1<<12);
#define RCC_APB2ENR (*(volatile unsigned int*)(0x40021000 + 0x18))
#define GPIOE_CRH (*(volatile unsigned int*)(0x40011800 + 0x04))
#define GPIOE_ODR (*(volatile unsigned int*)(0x40011800 + 0xC))
int main(void)
{
/*使能PE组端口的时钟*/
RCC_APB2ENR |= 1<<6; //RCC_APB2ENR = 3(类似)
GPIOE_CRH &= ~(0xf << 16); // 0b1111 == 0xf
GPIOE_CRH |= 1 << 16; /*输出,推挽*/
GPIOE_ODR &= ~(1<<12);
return 0;
}
学习STM32 的时候,拿到板子第一件事情
第一步: 电路图
第二步: 芯片的使用手册
第三步: 回到电路,看什么芯片,想驱动哪一个模块,看数据手册怎么驱动它
C语言对于 0x 40011800 + 0xC 认为是整型数据 用 * 修饰了以后就变为一个地址 ,编译器就知道是一个地址,但是往地址送东西,比如5 或者 6 是不行的,只能往存储单元送才可以,前面再加一个 * 号才能表示一个存储单元
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)