赞
踩
通过野火开发板学习单片机
在使用外设前,必须设置寄存器RCC_AHBENR
所有的被控单元FLASH、RAM、FSMC、AHB到APB的桥(片上外设)共同排列在一个4GB的地址空间,通过他们的地址来操作他们。
存储器本身不具有地址,是由芯片厂家或用户分配的,这个过程叫做存储器映射。
给存储器再分配一个地址叫做存储器重映射。
数据以小端格式存放在存储器中(最低地址在最低字节,最高地址在最高字节)
可访问的存储器空间背分成8个主要块,每个512MB。
Block0:用来设计成内部的FLASH
Block1:用来设计成内部的RAM
Block2:用来设计成片上的外设
寄存器映射
1 // GPIOB 端口全部输出高电平
2 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;
1 // GPIOB 端口全部输出高电平
2 #define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
3 * GPIOB_ODR = 0xFF;
为了方便操作,可以直接把指针操作* 也定义到寄存器别名里
1 // GPIOB 端口全部输出高电平
2 #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
3 GPIOB_ODR = 0xFF;
- GPIOx中的x都是从A-E
- 地址偏移:是相对于这个外设的基地址的偏移即GPIOA的外设基地址偏移就是0x40010800 + 0x10
- 寄存器位表,描述了每个bit的名称和权限以及对应的操作。y的取值是0-15,即对应GPIOA.1脚
- 注意对BRy写1:是清除,即设置低电平,写0无效
- 对BSy写1:是设置,即设置为高电平,写0无效
1 /* 外设基地址*/ 2 #define PERIPH_BASE ((unsigned int)0x40000000) 3 4 /* 总线基地址*/ 5 #define APB1PERIPH_BASE PERIPH_BASE 6 #define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) 7 #define AHBPERIPH_BASE (PERIPH_BASE + 0x00020000) 8 9 10 /* GPIO 外设基地址*/ 11 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) 12 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) 13 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) 14 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) 15 #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) 16 #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) 17 #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) 18 19 20 /* 寄存器基地址,以GPIOB 为例*/ 21 #define GPIOB_CRL (GPIOB_BASE+0x00) 22 #define GPIOB_CRH (GPIOB_BASE+0x04) 23 #define GPIOB_IDR (GPIOB_BASE+0x08) 24 #define GPIOB_ODR (GPIOB_BASE+0x0C) 25 #define GPIOB_BSRR (GPIOB_BASE+0x10) 26 #define GPIOB_BRR (GPIOB_BASE+0x14) 27 #define GPIOB_LCKR (GPIOB_BASE+0x18)
- 首先定义片上外设基地址PERIPH_BASE
- 接着在此基础上,加入个总线低地址偏移,得到APB1、APB2总线低基地址
- 在其上再加上外设地址的偏移,得到GPIOA-G的外设地址
- 最后在外设地址上假如各寄存器的地址偏移,得到特定寄存器的地址。
有了具体的地址,就可以使用指针读写该寄存器了。
1 /* 控制GPIOB 引脚0 输出低电平(BSRR 寄存器的BR0 置1) */
2 *(unsigned int *)GPIOB_BSRR = (0x01<<(16+0));
3
4 /* 控制GPIOB 引脚0 输出高电平(BSRR 寄存器的BS0 置1) */
5 *(unsigned int *)GPIOB_BSRR = 0x01<<0;
6
7 unsigned int temp;
8 /* 读取GPIOB 端口所有引脚的电平(读IDR 寄存器) */
9 temp = *(unsigned int *)GPIOB_IDR;
- 该代码使用(Unsigned int *)把GPIOB_BSRR宏的数值强制转换成了地址
- 再取* 并赋值,是对改地址的写操作。
- 读寄存器也是用取指针操作,把寄存器中的数据取到变量里,从而获取外设的状态。
1 typedef unsigned int uint32_t; /* 无符号32 位变量*/
2 typedef unsigned short int uint16_t; /* 无符号16 位变量*/
3
4 /* GPIO 寄存器列表*/
5 typedef struct {
6 uint32_t CRL; /*GPIO 端口配置低寄存器地址偏移: 0x00 */
7 uint32_t CRH; /*GPIO 端口配置高寄存器地址偏移: 0x04 */
8 uint32_t IDR; /*GPIO 数据输入寄存器地址偏移: 0x08 */
9 uint32_t ODR; /*GPIO 数据输出寄存器地址偏移: 0x0C */
10 uint32_t BSRR; /*GPIO 位设置/清除寄存器地址偏移: 0x10 */
11 uint32_t BRR; /*GPIO 端口位清除寄存器地址偏移: 0x14 */
12 uint16_t LCKR; /*GPIO 端口配置锁定寄存器地址偏移: 0x18 */
13 } GPIO_TypeDef;
- 使用GPIO_TypeDef结构体,其中的7个成员变量名就是对应的寄存器名称。
- 利用了C语言结构体内变量的存储空间是连续的,32位变量占4个字节,16位占2个字节
- 假如这个结构体首地址位0x40010C00,那么第二个成员变量就是+0x04
- 这样GPIO外设定义的寄存器地址就可以一一对应了,可以通过结构体的形式访问寄存器
1 GPIO_TypeDef * GPIOx; //定义一个GPIO_TypeDef 型结构体指针GPIOx
2 GPIOx = GPIOB_BASE; //把指针地址设置为宏GPIOB_BASE 地址
3 GPIOx->IDR = 0xFFFF;
4 GPIOx->ODR = 0xFFFF;
5
6
7 uint32_t temp;
8 temp = GPIOx->IDR; //读取GPIOB_IDR 寄存器的值到变量temp 中
- 先用 GPIO_TypeDef 类型定义一个结构体指针,并将其指向GPIOB_BASE
- 可以使用->ODR来读写寄存器。
1 /* 使用GPIO_TypeDef 把地址强制转换成指针*/ 2 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) 3 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) 4 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) 5 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) 6 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) 7 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) 8 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) 9 #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) 10 11 12 13 /* 使用定义好的宏直接访问*/ 14 /* 访问GPIOB 端口的寄存器*/ 15 GPIOB->BSRR = 0xFFFF; //通过指针访问并修改GPIOB_BSRR 寄存器 16 GPIOB->CRL = 0xFFFF; //修改GPIOB_CRL 寄存器 17 GPIOB->ODR =0xFFFF; //修改GPIOB_ODR 寄存器 18 19 uint32_t temp; 20 temp = GPIOB->IDR; //读取GPIOB_IDR 寄存器的值到变量temp 中 21 22 /* 访问GPIOA 端口的寄存器*/ 23 GPIOA->BSRR = 0xFFFF; 24 GPIOA->CRL = 0xFFFF; 25 GPIOA->ODR =0xFFFF; 26 27 uint32_t temp; 28 temp = GPIOA->IDR; //读取GPIOA_IDR 寄存器的值到变量temp 中
- 我们可以直接使用宏定义好各端口的首地址
- 使用时直接用该宏访问寄存器即可。
- 这部分的工作都已经由固件库帮我们完成了
例:假设a变量代表寄存器,假设已有数值,需要将其清零,其他位保持不变
1 //定义一个变量a = 1001 1111 b (二进制数)
2 unsigned char a = 0x9f;
3
4 //对bit2 清零
5
6 a &= ~(1<<2);
7
8 //括号中的1 左移两位,(1<<2) 得二进制数:0000 0100 b
9 //按位取反,~(1<<2) 得1111 1011 b
10 //假如a 中原来的值为二进制数: a = 1001 1111 b
11 //所得的数与a 作”位与&”运算,a = (1001 1111 b)&(1111 1011 b),
12 //经过运算后,a 的值a=1001 1011 b
13 // a 的bit2 位被被零,而其它位不变。
1 //若把a 中的二进制位分成2 个一组 2 //即bit0、bit1 为第0 组,bit2、bit3 为第1 组, 3 // bit4、bit5 为第2 组,bit6、bit7 为第3 组 4 //要对第1 组的bit2、bit3 清零 5 6 a &= ~(3<<2*1); 7 8 //括号中的3 左移两位,(3<<2*1) 得二进制数:0000 1100 b 9 //按位取反,~(3<<2*1) 得1111 0011 b 10 //假如a 中原来的值为二进制数: a = 1001 1111 b 11 //所得的数与a 作”位与&”运算,a = (1001 1111 b)&(1111 0011 b), 12 //经过运算后,a 的值a=1001 0011 b 13 // a 的第1 组的bit2、bit3 被清零,而其它位不变。 14 15 //上述(~(3<<2*1)) 中的(1) 即为组编号; 如清零第3 组bit6、bit7 此处应为3 16 //括号中的(2) 为每组的位数,每组有2 个二进制位; 若分成4 个一组,此处即为4 17 //括号中的(3) 是组内所有位都为1 时的值; 若分成4 个一组,此处即为二进制数“1111 b” 18 19 //例如对第2 组bit4、bit5 清零 20 a &= ~(3<<2*2);
1 //a = 1000 0011 b
2 //此时对清零后的第2 组bit4、bit5 设置成二进制数“01 b ”
3
4 a |= (1<<2*2);
5 //a = 1001 0011 b,成功设置了第2 组的值,其它组不变
1 //a = 1001 0011 b
2 //把bit6 取反,其它位不变
3
4 a ^=(1<<6);
5 //a = 1101 0011 b
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。