基本知识框架
课堂笔记
什么是寄存器开发
使用STM32,由于其内部的系统结构无需经常变化,所以大部分时候的STM32的开发,是和外设打交道。而操作外设需要通过操作寄存器去间接实现,所以STM32开发也可以看作是寄存器开发
什么是库开发
首先要知道什么是库,库就是一系列具有通用性函数和二进制的集合
库分为静态库和动态库
静态库和动态库的文件形式
- Windows:静态库 xx.lib || 动态库 xx.dll
- LInux: 静态库 xx.a. || 动态库 xx.so
静态库和动态库使用方法
- 静态库:使用时,对应静态链接操作,在生成可执行程序的链接环节,会被链接到程序中去,最终生成可执行文件
- 动态库:使用时,对应动态链接操作,相对于静态链接,动态链接的文件和可执行程序是相对独立的,但可执行程序中有动态库的位置信息和其中函数的接口信息,据此找到动态库并实现函数调用
STM32开发中的标准库是ST公司编写的,属于静态库,里面包含了外设相关的所有函数和相关结构体定义等等,格式标准规范,实现方式优雅
研究STM32的标准库,对于学习STM32是很有帮助的
寄存器开发和库开发的关联
寄存器开发的基本流程
- 首先要根据要开发的功能,查阅外设的数据手册,找到相应的寄存器
- 根据寄存器的说明。逐位进行寄存器的读写配置
- 后期维护或者调试,也需要根据外设的数据手册,进行纠错
寄存器开发的优缺点
优点: 程序运行效率高,需要那个寄存器就配置那个寄存器,冗余代码量少 缺点: 如果遇到下列情况,使用寄存器开发的难度会大大提高
- 遇到大型项目或者需配置寄存器数量较多时,需要频繁翻阅数据手册,影响开发效率。过于依赖数据手册
- 程序如果需要移植,那么底层很多的寄存器操作需要重写。程序可移植性差
- 后期维护后者调试的时候,如果没有数据手册,很多寄存器的读写很不好理解。程序可读性差
库开发的基本流程
- 根据要开发的功能,查看库接口文档,找到所需的函数,结构体或者宏定义
- 调用相应的函数接口,声明结构体或者使用宏定义的方式去实现功能
- 后期维护和调试,由于重新封装了直观的函数,结构体和宏定义名称,可以不用过多参考库接口文档
库开发的优缺点
优点: 相对于寄存器开发,在遇到如上相似的情况时,库开发就更具优势
- 遇到大型项目或者需配置寄存器数量较多时,可以直观的操作想要的寄存器。开发效率较高
- 程序如果需要移植,底层只需要进行小的改动即可在新平台上使用。程序的可移植性较高
- 后期维护或者调试,不过于依赖技术手册。程序可读性高
缺点: 由于库对底层的寄存器,某些结构体等重新进行了宏定义的类型定义,所以抽象结构上多了一层库函数层,实际程序运行时也需要处理更多的代码
结论
随着技术的进步和社会需求的提高,STM32的需要调用的外设资源会越来越多,且要处理的项目也会趋于大型化和复杂化,这给项目的前期搭建和后期维护带来很大的挑战——如何高效的调用资源实现需求,如何高效的修改程序Debug
库开发使用了封装的概念很好的解决了这些问题。封装就是把一个抽象的事物的属性及属性相关的操作函数打包在一起,外界的模块只能通过这个抽象事物对外提供的函数接口,对事物的属性进行访问。封装使得上层使用者只需要调用接口,无需过于关心寄存器操作是怎么实现的,从而更高效的解决需求
如何从寄存器开发实现库开发(GPIO口为例,通过操作其寄存器点亮LED灯)
使用STM32CubeIDE来建立项目,通过项目的实践可以更好的理解库函数是怎么工作的 一般来说简单项目包含以下文件:
- main.c主资源文件
- 库相关文件(*.h头文件+*.c资源文件 或 封装后的.lib库文件)
下面以GPIO寄存器的操作为例,从寄存器开发实现基础库开发
寄存器基本位操作
要进行寄存器开发,首先要了解基本的位操作
置位操作
使用按位操作符实现置位
int GPIO = 0x00000000;
GPIO |= 1<<4;
说明:1<<4后得到0x00001000,|=使得只有GPIO第4位置成了1,其他位不受影响
复位操作
使用按位与操作符实现复位
int GPIO = 0x11111111;
GPIO &= ~(1<<4);
说明:1<<4后得到0x00001000,~(1<<4)后得到0x11110111,&=使得只有GPIO第4位置成了0,其他位不受影响
反转位操作
使用按位异或操作符实现位反转
int GPIO = 0x00000000;
GPIO ^= 1<<4;
说明:1<<4后得到0x00001000,^=使得只有GPIO第4位置成了1(如果第4位是1,则会被置成0),其他位不受影响
寄存器实现方式
main.c的代码主体部分实现
#include <stdio.h>
int main(void)
{
*(unsigned int*)(0x40021018) |= (0x01 << 3);
*(unsigned int*)(0x40010c00) &= ~(0x0F << 4);
*(unsigned int*)(0x40010c00) &= ~(0x01 << 4);
*(unsigned int*)(0x40010c0c) |= (0 << 0);
while(1);
}
封装 外设存储器地址
主要目的:
头文件stm32f10x.h代码主体部分实现
#define __IO volatile
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APBP1ERIPH_BASE (PERIPH_BASE + 0x00000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
封装 GPIO基地址 / 外设寄存器结构体 / GPIO输入输出模式枚举变量
主要目的:
- 为GPIO基地址重新定义宏名,方便访问GPIO
- 定义GPIO寄存器结构体,方便访问寄存器
- 使用枚举变量定义GPIO输入输出模式 ,输出速率
头文件stm32f10x_gpio.h代码主体部分实现
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
typedef enum
{
GPIO_Mode_ANALOG_IN = 0x00,
GPIO_Mode_FLOATING_IN = 0x04,
GPIO_Mode_INU = 0x28,
GPIO_Mode_IND = 0x48,
GPIO_Mode_OUT_OD = 0x14,
GPIO_Mode_OUT_PP = 0x10,
GPIO_Mode_AF_OD = 0x1c,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSSR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
}GPIO_TypeDef;
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0c00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1c00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
#define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef*)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef*)GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef*)GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef*)GPIOG_BASE)
#define RCC_BASE *(AHBPERIPH_BASE + 0x1000)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
封装GPIO操作函数
主要目的:
- 定义GPIO操作函数,可以通过函数控制GPIO口
- 定义GPIO初始化函数,可以通过函数初始化GPIO口
资源文件stm32f10x_gpio.c代码主体部分实现
#include <stm32f10x_gpio.h>
void GPIO_SetBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_PIN)
{
GPIOx->BSRR = GPIO_PIN;
}
void GPIO_ResetBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_PIN)
{
GPIOx->BRR = GPIO_PIN;
}
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStructure)
{
uint32_t currentmode = 0x00;
uint32_t tmpreg = 0x00;
uint32_t currentgroup = 0x00;
uint32_t currentpin = 0x00;
currentmode = ((uint32_t)GPIO_InitStructure->GPIO_Mode) & ((uint32_t)0x0F);
if (0x00 != (((uint32_t)GPIO_InitStructure->GPIO_Mode) & ((uint32_t)0x10)))
{
currentmode |= (uint32_t)GPIO_InitStructure->GPIO_Speed;
}
if (0x00 != (((uint32_t)GPIO_InitStructure->GPIO_PIN) & (uint32_t)0x00FF))
{
tmpreg = GPIOx->CRL;
for (currentgroup=0x00; currentgroup<0x08; currentgroup++)
{
currentpin = (uint32_t)0x01 << currentgroup;
if (0x00 != ((uint32_t)GPIO_InitStructure->GPIO_PIN & (uint32_t)currentpin))
{
tmpreg &= ~((uint32_t)(0x0F <<(currentgroup << 2)));
tmpreg |= (currentmode << (currentgroup << 2));
if (GPIO_Mode_INU == GPIO_InitStructure->GPIO_Mode)
GPIOx->BSRR = (uint32_t)0x01 << currentgroup;
else if (GPIO_Mode_IND == GPIO_InitStructure->GPIO_Mode)
GPIOx->BRR = (uint32_t)0x01 << currentgroup;
}
}
GPIOx->CRL = tmpreg;
}
if ((uint32_t)GPIO_InitStructure->GPIO_PIN > 0x00FF)
{
tmpreg = GPIOx->CRH;
for (currentgroup=0x08; currentgroup<0x10; currentgroup++)
{
currentpin = (uint32_t)0x01 << currentgroup;
if (0x00 != ((uint32_t)GPIO_InitStructure->GPIO_PIN & (uint32_t)currentpin))
{
tmpreg &= ~(uint32_t)(0x0F <<(currentgroup << 2));
tmpreg |= (currentmode << (currentgroup << 2));
if (GPIO_Mode_INU == GPIO_InitStructure->GPIO_Mode)
GPIOx->BSRR = (uint32_t)0x01 << currentgroup;
else if (GPIO_Mode_IND == GPIO_InitStructure->GPIO_Mode)
GPIOx->BRR = (uint32_t)0x01 << currentgroup;
}
}
GPIOx->CRH = tmpreg;
}
}
基本库实现方式
在经过封装后,就完成了最基础的库的构建,通过这个很基础的库可以更快更清晰地实现开发者的需求,省去很多开发或者调试中很多不必要的工作量
除了GPIO口,STM32上还有许多其他外设,但无需开发者再去为其单独开发库,ST官方已经为开发者提供了官方版本的外设库,在进行开发时只要根据需求直接使用即可
对于开发者,在一定阶段后可以对ST官方库进行研究。官方库实现的方式相当的优雅和严谨,研究官方库的具体实现,可以对STM32开发有很大的帮助,同时也可以提升C语言的能力
main.c的代码主体部分实现
#include <stm32f10x.h>
#include <stm32f10x_gpio.h>
void SystemInit(void);
int main(void)
{
GPIO_InitTypeDef GPIOB_ForLED;
RCC_APB2ENR |= (0x01 << 3);
GPIOB_ForLED.GPIO_PIN = GPIO_PIN_0;
GPIOB_ForLED.GPIO_Speed = GPIO_Speed_50MHz;
GPIOB_ForLED.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIOB_ForLED);
GPIO_ResetBit(GPIOB, GPIO_PIN_0);
while(1);
}
void SystemInit(void)
{
}
基本知识框架Xmind文件下载
链接:资源下载
|