ARM寄存器食用感受
最近研究NXP新推出的kinetis KV58微控制器,用的是NXP官方推出的MCUXpresso的SDK,研究后发现并没有想象的好用,故决定还是手撸寄存器好了.
MKV58F24.h
首先需要关注这个头文件,该头文件中包含了KV58微控制器所有寄存器地址,是最重要的头文件.下面以GPIO为例,介绍一下撸寄存器的方法.操作每个模块主要可以分为3部分.
此外,手册中还定义了其他内容,如中断向量表,DMA触发源表,编译器设置,版本信息等等.
地址定义
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | 
 
 
 
 
 
 #define GPIOA_BASE                               (0x400FF000u)
 
 #define GPIOA                                    ((GPIO_Type *)GPIOA_BASE)
 
 #define GPIOB_BASE                               (0x400FF040u)
 
 #define GPIOB                                    ((GPIO_Type *)GPIOB_BASE)
 
 #define GPIOC_BASE                               (0x400FF080u)
 
 #define GPIOC                                    ((GPIO_Type *)GPIOC_BASE)
 
 #define GPIOD_BASE                               (0x400FF0C0u)
 
 #define GPIOD                                    ((GPIO_Type *)GPIOD_BASE)
 
 #define GPIOE_BASE                               (0x400FF100u)
 
 #define GPIOE                                    ((GPIO_Type *)GPIOE_BASE)
 
 #define GPIO_BASE_ADDRS                          { GPIOA_BASE, GPIOB_BASE, GPIOC_BASE, GPIOD_BASE, GPIOE_BASE }
 
 #define GPIO_BASE_PTRS                           { GPIOA, GPIOB, GPIOC, GPIOD, GPIOE }
 
 
 
 
 
 
 | 
 可以看出,文件中定义了各gpio寄存器的首地址GPIOx_BASE,定义了指向首地址的指针GPIOx.使用宏定义的方式可以显著减少代码量,采用首地址加偏移量的方式就可以访问各个寄存器了.
寄存器结构体
 | 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 |  
 
 
 
 
 typedef struct {
 __IO uint32_t PDOR;
 __O  uint32_t PSOR;
 __O  uint32_t PCOR;
 __O  uint32_t PTOR;
 __I  uint32_t PDIR;
 __IO uint32_t PDDR;
 } GPIO_Type;
 
 
 | 
这就是gpio类型的结构体,gpio类型的指针可以访问寄存器,以首地址加偏移量的方式就可以访问整个寄存器中任意位置.值得注意的是其中的__IO和__o,它们意义如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | #ifdef __cplusplus
 #define   __I     volatile
 #else
 #define   __I     volatile const
 #endif
 #define     __O     volatile
 #define     __IO    volatile
 
 
 | 
操作掩码宏定义
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
 | * ------------------------------------------------------------------------------ GPIO Register Masks
 ---------------------------------------------------------------------------- */
 
 
 
 
 
 
 
 #define GPIO_PDOR_PDO_MASK                       (0xFFFFFFFFU)
 #define GPIO_PDOR_PDO_SHIFT                      (0U)
 #define GPIO_PDOR_PDO(x)                         (((uint32_t)(((uint32_t)(x)) << GPIO_PDOR_PDO_SHIFT)) & GPIO_PDOR_PDO_MASK)
 
 
 #define GPIO_PSOR_PTSO_MASK                      (0xFFFFFFFFU)
 #define GPIO_PSOR_PTSO_SHIFT                     (0U)
 #define GPIO_PSOR_PTSO(x)                        (((uint32_t)(((uint32_t)(x)) << GPIO_PSOR_PTSO_SHIFT)) & GPIO_PSOR_PTSO_MASK)
 
 
 #define GPIO_PCOR_PTCO_MASK                      (0xFFFFFFFFU)
 #define GPIO_PCOR_PTCO_SHIFT                     (0U)
 #define GPIO_PCOR_PTCO(x)                        (((uint32_t)(((uint32_t)(x)) << GPIO_PCOR_PTCO_SHIFT)) & GPIO_PCOR_PTCO_MASK)
 
 
 #define GPIO_PTOR_PTTO_MASK                      (0xFFFFFFFFU)
 #define GPIO_PTOR_PTTO_SHIFT                     (0U)
 #define GPIO_PTOR_PTTO(x)                        (((uint32_t)(((uint32_t)(x)) << GPIO_PTOR_PTTO_SHIFT)) & GPIO_PTOR_PTTO_MASK)
 
 
 #define GPIO_PDIR_PDI_MASK                       (0xFFFFFFFFU)
 #define GPIO_PDIR_PDI_SHIFT                      (0U)
 #define GPIO_PDIR_PDI(x)                         (((uint32_t)(((uint32_t)(x)) << GPIO_PDIR_PDI_SHIFT)) & GPIO_PDIR_PDI_MASK)
 
 
 #define GPIO_PDDR_PDD_MASK                       (0xFFFFFFFFU)
 #define GPIO_PDDR_PDD_SHIFT                      (0U)
 #define GPIO_PDDR_PDD(x)                         (((uint32_t)(((uint32_t)(x)) << GPIO_PDDR_PDD_SHIFT)) & GPIO_PDDR_PDD_MASK)
 
 
 
 
 
 
 
 | 
 这部分主要定义了各种掩码和偏移量,方便操作寄存器,减少代码书写量.
MASK SHIFT (X)是什么
 开始看寄存器最不理解的就是掩码宏定义这一部分,不过各种掩码操作熟练以后确实能够很高效的操作寄存器.
MASK
 mask本意为遮挡,观察后发现掩码用于给某一寄存器赋1或0.
 比如将SPI当中的MTFE寄存器赋值1,只需要:
 | 12
 
 | SPI_MCR_REG(SPIN[spin]) = ( 0 | SPI_MCR_HALT_MASK);        
 
 | 
如果需要将寄存器赋值0,只需要:
 | 12
 
 | SPI_MCR_REG(SPIN[spin]) = ( 0 & ~SPI_MCR_HALT_MASK);        
 
 | 
shift
 注意shift当中的值都是用十进制表示的,在这里,shift表示偏移量,就是寄存器相对首地址偏移的位置.
(x)
 配合shift和mask,操作寄存器就变得简单了,这里(x)的意思就是将这个位置0或1,写法如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | SPI_MCR_REG(SPIN[spin]) =  (0|  SPI_MCR_PCSIS(pcs));
 
 
 SPI_MCR_REG(SPIN[spin]) =  (0 &  ~SPI_MCR_PCSIS(pcs));
 
 
 
 
 typedef enum
 {
 SPI_PCS0 = 1 << 0,
 SPI_PCS1 = 1 << 1,
 SPI_PCS2 = 1 << 2,
 SPI_PCS3 = 1 << 3,
 SPI_PCS4 = 1 << 4,
 SPI_PCS5 = 1 << 5,
 } SPI_PCSn_e;
 
 | 
KV5x Sub-Family Reference Manual
https://www.nxp.com/docs/en/reference-manual/KV5XP144M240RM.pdf
KV5x Data Sheet
https://www.nxp.com/docs/en/data-sheet/KV5XP144M240.pdf