在研究生时段时,曾经对STM32进行了学习,在本科时段学习51系列的简单单片机,这种单片机功能比较单一,也没有多外设来使用,在进入研究生阶段由于实验室的关系,接触了PIC系列的单片机,PIC单片机编程比较简单,外设也丰富,完全可以按照数据手册使用起来。这种单片机价格也比较贵,适合在工业场合使用。而在32位的单片机中,STM32比较流行,STM32是使用的ARM内核,不同于51 和PIC,因此学习的东西也是比较多的。好吧,闲话就不多说了,把自己的理解记录下来吧,供以后复习和大家参考。
一、首先要明白,对于单片机,不论是8位的还是16位、32位的,主要的工作是驱动外设和实现功能。实现功能主要是程序的特定应用,而驱动外设是将连接到外设的设备或外设本身驱动起来,让其能够工作。对于没有实时系统的单片机程序,驱动外设主要是对特定外设功能的寄存器进行操作。控制了寄存器就控制了单片机。
操作寄存器首先要知道寄存器的地址,ARM寄存器的地址映射到统一的存储器地址中。这个对每个寄存器的地址在芯片数据手册中有说明。但一般还是不直接通过地址来操作寄存器,应为32位的寄存器比较复杂,ST公司对每个芯片提供的函数库,函数库就是对操作特定功能寄存器进行了封装。省去的大量的工作。我们直接进行库函数驱动外设就可以了。
但是,在进行函数库编程前,还是要知道它是如何进行的,例如:
GPIOB端口的数据寄存器输出,ODR的地址是0x4001 0C0C。
(1)采用绝对地址直接访问时, *(unsigned int*)(0x4001 0C0C)=0xFFFF;
(2)通过寄存器别名访问, #defined GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
*GPIOB_ODR=0xFF;
(3)通过寄存器别名访问, #defined GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
GPIOB_ODR=0xFF;
上面的三种访问寄存器的方法是很关键的C语言知识,其实是通过定义一个指针,把寄存器的地址赋值给指针,使用指针变量指向寄存器的单元。我们要知道每一个外设都有地址,知道地址才能准确的操作。
二、GPIO有多个控制、操作的寄存器,且每一组寄存器都有相同的寄存器,只是地址进行了偏移,所以引入结构体进行封装:
typedef struct{
unit32_t CRL; // 端口配置低寄存器
unit32_t CRH; // 端口配置高寄存器
uint32_t IDR; // 数据输入寄存器
unit32_t ODR; // 数据输出寄存器
unint32_t BSRR; //位设置、清除寄存器
unint32_t BRR; // 位清除寄存器
unint32_t LCKP; // 端口配置锁存寄存器
}
GPIO_TypeDef;
这对于GPIOA到GPIOE都是一样的。
要操作那组寄存器,只需要将地址赋值就可以操作,例如 #define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)
GPIOA->CRL=0xFFFF;
GPIOA->ODR=0xFFFF;
// 使用GPIO_TypeDef 把地址强制转换成指针//,明白了这些,我们就可以通过寄存器操作外设了。
三、简单操作寄存器来驱动外设
(1)软件 MDK5;
(2)硬件STM32F1开发版
(3)新建工程:startup_stm32f10x_hd.s main.c stm32f10x.h 这三个文件添加在工程项目中,stm32f10x.h 中对寄存器进行定义,main.c 中包含此文件。
在main.c 中操作GPIO来控制led灯, 操作GPIO有三部曲要知道:
1、打开GPIO 端口时钟
2、配置端口输入、输出方向
3、端口数据寄存器赋值。