公司开发需要用到stm32,nxp,gd32等系列的单片机,大学几乎没接触过单片机的学习,所以只能自己下来慢慢学,如有错误,欢迎指出。
ARM(公司,处理器,架构)
ARM处理器家族
- 经典:ARM7,ARM9,ARM11
- Cortex-M:单片机驱动(控制)
- Cortex-A:开发式系统的高性能处理器(智能手机 ,数字电视)
- Cortex-R:实时系统,实时的控制需求(汽车制动,动力系统)
想要操作stm32的外设通常可以使用寄存器或者stm32库,在当今stm32库已经十分成熟,并且操作十分简单。
寄存器:寄存器则是内置于各个 IP 外设中,是一种用于配置外设功能的存储器,就是一种内存,并且有相对应的地址。
GPIO:通用输入输出接口
按键:通过输入电平的高低感知来得到按键的按下与否。
led灯:通过输出高低电平控制led的亮与灭。
GPIO也可以作为串口数据的收发管脚或AD接口等复用功能,根据数据手册配置相应的功能寄存器。
GPIO寄存器MODER
32位两位为一组,分为了16个,分别对应每一组GPIO的16个 管脚。如PA0-PA15。
点灯:学习嵌入式开发最开始的操作就是点灯,以下使用寄存器操作和库函数操作来点灯。
寄存器:从技术参考手册中可以找到ODR 寄存器的地址偏移是: 0CH,这个偏移地址是基于端口的起始地址而言的。在 STM32 中,每个外设都有一个起始地址,叫做外设基址,外设的寄存器就以这个基地址为标准按照顺序排列,跟结构体里面的成员差不多。
假设我们需要点亮的灯是由GPIOB-0这个管脚去控制的,找到GPIOB对应的基地址,再加上ODR的偏移地址就可以去实现输出功能。
#define RCC_APB2ENR *(volatile unsigned long *)0x40021018 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00 #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C int main( void ) { // 开启端口 B 的时钟 RCC_APB2ENR |= 1<<3; // 配置 PB0 为通用推挽输出模式,速率为 2M GPIOB_CRL = (2<<0) | (0<<2); // PB0 输出低电平,点亮 LED GPIOB_ODR = 0<<0; } void SystemInit( void ) { }
这种就是比较愚蠢的配置方式,每次配置一个外设就去从参考手册中去找,浪费时间且容易出错,但新手入门是需要理解并且操作学习一下的。
如果要以寄存器配置的话,会将所有的寄存器位置都先定义好,这样就不用一个个去找。实际开发过程中很少使用寄存器操作,一般都是库函数的操作,下面介绍库的操作。
keil的stm32环境自行去百度配置,篇幅太长就不写出了。
固件库分析:看看每个文件的作用是什么,这对我们能否清晰的调用库函数编程非常重要。
STM32 由 Cortex-M3 内核和内核之外的各种外设组成,库在编写的时候也遵循这中组成结构,把代码分成两大部分,一种是操作内核外设的,
另外一种是内核之外的外设,为了听起来不那么绕,下面我们把内核之外的外设用处理器外设来代替。
- startup_stm32f10x_hd.s:
这个是由汇编编写的启动文件,是 STM32 上电启动的第一个程序,启动文件主要实现了:1、初始化堆栈指针SP;2、设置 PC 指针 =Reset_Handler ;3、设置向量表的地址,并初始化向量表,向量表里面放的是 STM32 所有中断函数的入口地址4、调用库函数SystemInit ,把系统时钟配置成 72M ,SystemInit 在库文件 stytem_stm32f10x.c 中定义;5、跳转到标号 _main,最终去到 C 的世界。
- system_stm32f10x.c:
这个文件的作用是里面实现了各种常用的系统时钟设置函数,有 72M,56M, 48,36,24,8M,我们使用的是把系统时钟设置成 72M。
- stm32f10x.h:
这个头文件非常重要,可以说是上帝之手。这个头文件实现了: 1、处理器外设寄存器的结构体定义 2、处理器外设的内存映射 3、处理器外设寄存器的位定义。
- stm32f10x_xxx.h:
外设 xxx 应用函数库头文件,这里面主要定义了实现外设某一功能的结构体,比如通用定时器有很多功能,有定时功能,有输出比较功能,有输入捕捉功能,而通用定时器有非常多的寄存器要实现某一个功能,比如定时功能,我们根本不知道具体要操作哪些寄存器,这个头文件就为我们打包好了要实现某一个功能的寄存器,是以结构体的形式定义的,比如通用定时器要实现一个定时的功能,我们只需要初始化TIM_TimeBaseInitTypeDef 这个结构体里面的成员即可,里面的成员就是定时所需要操作的寄存器。 有了这个头文件,我们就知道要实现某个功能需要操作哪些寄存器,然后再回手册中精度这些寄存器的说明即可。
- stm32f10x_xxx.c :
外设 xxx 应用函数库,这里面写好了操作 xxx 外设的所有常用的函数,我们使用库编程的时候,使用的最多的就是这里的函数。
- cor_cm3.h:
这个头文件实现了: 1、内核结构体寄存器定义 2、内核寄存器内存映射 3、内存寄存器位定义。跟处理器相关的头文件 stm32f10x.h 实现的功能一样,一个是针对内核的寄存器,一个是针对内核之外,即处理器的寄存器。
- misc.c:
内核应用函数库文件,对应 stm32f10x_xxx.c 。在 CM3 这个内核里面还有一些功能组件,如 NVIC、SCB、ITM 、MPU、CoreDebug ,CM3 带有非常丰富的功能组件,但是芯片厂商在设计 MCU 的时候有一些并不是非要不可的,是可裁剪的,比如 MPU、ITM 等在STM32 里面就没有。其中 NVIC 在每一个 CM3 内核的单片机中都会有,但都会被裁剪,只能是 CM3 NVIC 的一个子集。在 NVIC 里面还有一个 SysTick,是一个系统定时器,可以提供时基,一般为操作系统定时器所用。
-
misc.h:内核应用函数库头文件,对应 stm32f10x_xxx.h 。
直接写库函数操作了:
void LED_GPIO_Config(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启GPIOC的外设时钟*/ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); /*选择要控制的GPIOB引脚*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; /*调用库函数,初始化GPIOB*/ GPIO_Init(GPIOB, &GPIO_InitStructure); /* 关闭led灯 */ GPIO_SetBits(GPIOB, GPIO_Pin_0);
/* 开启led灯 */
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}
typedef struct { uint16_t GPIO_Pin; /*指定将要进行配置的 GPIO 引脚* GPIOSpeed_TypeDef GPIO_Speed; /*指定 GPIO 引脚可输出的最高频率*/ GPIOMode_TypeDef GPIO_Mode; /*指定 GPIO 引脚将要配置成的工作状态*/ }GPIO_InitTypeDef;
Mode和Speed相关结构体也是被设定好的,可以自行查找这里就不写出来了,直接赋值就可以了,十分方便并且相对于寄存器操作便于理解。
GPIO_Init(GPIO_TypeDef *, GPIO_InitTypeDef *);第一个参数,说明它将要对GPIOB 端口进行初始化。初始化的配置以第二个参数 GPIO_InitStructure 结构 体的成员值为准。这个结构体的成员,我们在调用GPIO_Init()前,已对它们 赋予了控制参数。