
意法半导体ST与ARM的关系 ST公司购买ARM公司的内核,在此基础上添加各种外设,组成STM32芯片 也就是说,在内核之上,所有的外设都是ST公司添加上去的 ST内核通过总线矩阵与外设连接 ![]() 嵌入式C语言中对于地址的操作 ![]() 此时指针pointer是指向寄存器的地址0x4002800 ![]() 按照这样我们可以完成对地址的访问 这些基于指针的地址操作是非常常见的,基于寄存器的编程就是通过指针完成的 进一步地,考虑到定义变量会消耗内存,如果我们大量地定义地址变量,将会消耗大量的内存空间 因此可以使用宏定义,因为宏定义只是纯粹的文本替换 如下图所示 ![]() 实际使用中,将GPIO外设的基地址,通过强制类型转换,将其转换成结构体指针的形式 可以简单理解为,建立了结构体与实际内存中的一种映射关系 访问结构体成员(通过->),就等同于访问实际的一小块对应地址 HAL库中GPIO的配置 HAL库中对于GPIO的配置如下 ![]() 为了理解上述代码,我们可以先看以下代码 ![]() 在上面的代码中,我们首先将1左移3位,然后将其以或运算的方式对寄存器中的内容进行操作,这样就打开了端口B时钟,对应的手册内容如下: ![]() 这里对于寄存器地址是以基地址+偏移的形式进行定义的 ![]() 这个寄存器的低4位,即0 1 2 3位对应0端口,即第0位,对应于GPIO的0 端口 假设我们的需求是:将最低2位配置为10 ,最低3,4位配置为00 也就是说我们的需求是:将寄存器的最低4位配置为2 一种做法是,我们直接将寄存器或上1,且左移0位,即:
这样子,最低第2位就是1了 但是这样的操作存在如下问题:因为假设最低4位本来就有数值的话,比如最低4位是1110,那么你直接进行这样的操作,其实只改了最低2位,而最低第3,4位仍然是11,并没有清0。 因此,在此之前,我们还需要对寄存器待配置的位进行清0操作
在这是,因为一次我们是操作4位,比如操作端口0时,我们用到的是第0,1,2,3位,操作端口1,我们用到的是4,5,6,7位,操作端口2,我们用到的是8,9,10,11位, 我们每次都是4位一起操作,所以左移了4*0,如果我们操作端口1,即4,5,6,7位,那么我们就可以左移4*1 0xf是4个1,这个数是整型变量,因此会占32位宽度。也就是说此时除了低4位是1,其他的全部都是0,这时候我们将其取反,则32位中除了低4位是0,其他全部是1。 此时,我们再将这个数整体与到寄存器中,此时这个数的最低4位全是0,与上去之后,相对于寄存器的最低4位被清0了,而其他位保持不变 清完0之后再将我们要的数据或上去,那么我们想要改变的位就会发生改变了,而且不影响其他的位 ![]() ![]() STM32的板级支持包 板级支持包 (BSP) (Board Support Package) 是介于主板硬件和操作系统中驱动层程序之间的一层,一般认为它属于操作系统一部分,主要是实现对操作系统的支持,为上层的驱动程序提供访问硬件设备寄存器的函数包,使之能够更好的运行于硬件主板 ![]() 板级支持包:对板上的资源功能给出实现,并且提供用户应用程序的接口。以LED灯为例,用户应用程序不需要知道GPIO的硬件特点,他只需要知道调用这个函数,就可以点亮LED灯 我们可以自己定义BSP中的宏 同样,我们可以在main函数中将板级支持包include进来 注意:板级支持包的include位置 ![]() ![]() ARM Cortex M3的哈佛架构 取指和数据分开 ![]() 对于处理器而言,可以将其理解为没有感情的代码执行机器。只要处理器开始工作,就会不断地获 取指令,并且执行 Cortex M3对指令集的丰富 ![]() STM32的启动过程 STM32芯片上电以后会触发复位异常 在中断向量表中,处理器根据触发的异常,跳转到中断向量表的特定位置,获取内容并执行 即流程为: 触发异常->中断向量表->用户程序 STM32的中断优先级如下: ![]() STM32 Cortex M3中断向量表 ![]() STM32的启动文件startup_stm32l432xx.s 以STML4为例,其启动文件为startup_stm32l432xx.s 可以查看其注释里的简介
第一步 设置堆栈的指针 第二步 设置PC指针的值,PC指针指向当前指令+8的位置 第三步 设置中断向量表的入口 第四步 配置系统时钟 第五步 调main函数 首先,设置堆栈大小 ![]() 然后,设置PC指针 注意:DCD指令会为指定的目标分配一块以字为单位的空间,一个字就是4个字节 首先为初始化堆栈分配1个字的空间,然后将对应的内容放进去 ![]() 此即复位异常 ![]() ![]() 在这个Reset_Handler即复位中断的服务函数,其中IMPORT了两个函数,即__main和SystemInit 先将SystemInit函数的地址(即函数名)放到R0寄存器,然后BLX指令跳转到R0寄存器去执行 同样,将__main函数的地址(即函数名)放到R0寄存器,注意接下来使用的是BX跳转,跳过去就不会跳转回来了 ![]() 注意,汇编语言中的[WEAK ]表示弱定义,即我们可以自己去实现的 ———————————————— 版权声明:CodeForCoffee |
【有奖体验】STM32Cube通过Clang/LLVM支持进一步简化代码开发
STM32CubeMX应用结构选择指南
经验分享 | STM32CubeMX 生成时钟获取函数的分析
兔哥的最强U5图显【000】——U5G9最小系统绘制
兔哥的ST67——【000】ST67模组订购
基于LORA的环境感知系统
经验分享 | 使用CubeMx配置NVIC时为何不见子优先级?
经验分享 | 三个 ADC 同步模式配置以及 CubeMx 错误配置的解决方法
兔哥的边缘AI【001】——DIY-STM32N6全IO扩展板
兔哥的BLE【002】-WB09最小系统板PCB设计