1.用结构体方式构建库
首先解释一下如何让本指向此寄存器第一位的指针指向全部寄存器
GPIOB_BASE地址为0x40010C00,和GPIOB_CDL地址是一样的
经过宏定义可知GPIOB_BASE是地址,地址就是指针,所以GPIOB_BASE是一个指针,其内存是32位(在stm32中,外设的寄存器的长度都是32位的,因此指针的类型也是32位),GPIOB也是寄存器,所以内存也是32位,内存大小刚好可以对应起来,那么可以实现通过GPIOB_BASE访问整个GPIOB,而不是其中的CDL,为了达到这个目的,将GPIOB_BASE强制类型转换为GPIO_TypeDef类型。(GPIO_TypeDef*)GPIOB_BASE
建结构体犯得错
结构体里的变量是有数据类型的,别乱写
定义宏的时候写上名儿,不然你定义谁呢,服了
写反了结构体名字 ,导致主函数无法用->方式调用CRL寄存器,依葫芦画瓢还能写反了,6
作业
typedef struct{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
}RCC_Typedef;
#define RCC ((RCC_Typedef*)RCC_BASE)
用函数的方式点灯
新建置位BSRR,清零BRR函数
void bitset(gpiob,pb0)置一(关灯){
直接让位设置寄存器=pb0(所以要先定义好pb0这块的宏)
}
2.防止重复定义头文件
解决办法:添加如下代码
每写一个头文件,都记得添加一个宏,一般以文件的名字定义宏
防止重复定义头文件不是让你可以省略#include这一步,别忘了#include “stm32f10x,h”
#ifndef_STM32F10X_GPIO_H
#def_STM32F10X_GPIO_H
#endif/*_STM32F10X_GPIO_H*/
别写错头文件
是if not define,别写丢了
3.另一种方式实现置一操作点亮LED
自己构建库函数
定义如下宏文件
#define GPIO_PIN_1 ((uint16_t)0x0001) //00000000 00000001
#define GPIO_PIN_2 ((uint16_t)0x0002) //00000000 00000010
#define GPIO_PIN_3 ((uint16_t)0x0004) //00000000 00000100
#define GPIO_PIN_4 ((uint16_t)0x0008) //00000000 00001000
#define GPIO_PIN_5 ((uint16_t)0x0010) //00000000 00010000
#define GPIO_PIN_6 ((uint16_t)0x0020) //00000000 00100000
#define GPIO_PIN_7 ((uint16_t)0x0040) //00000000 01000000
#define GPIO_PIN_8 ((uint16_t)0x0080) //00000000 10000000
#define GPIO_PIN_9 ((uint16_t)0x0100) //00000001 00000000
#define GPIO_PIN_10 ((uint16_t)0x0200) //00000010 00000000
#define GPIO_PIN_11 ((uint16_t)0x0400) //00000100 00000000
#define GPIO_PIN_12 ((uint16_t)0x0800 //00001000 00000000
#define GPIO_PIN_13 ((uint16_t)0x1000) //00010000 00000000
#define GPIO_PIN_14 ((uint16_t)0x2000) //00100000 00000000
#define GPIO_PIN_15 ((uint16_t)0x4000) //01000000 00000000
#define GPIO_PIN_16 ((uint16_t)0x8000) //10000000 00000000
定义的时候别忘了写上数据类型
干嘛呢,这是
不需要特意指定x是几,所有的GPIO都有这些引脚
新建函数
错误一:函数变量与内部不对应
void GPIO_Setbits(GPIO_TypeDef*GPIOx,uint16_t GPIO_PIN_x)
{
GPIO->BSRR |=GPIO_PIN;
}
正确的
利用BSR
0:对应ODRy位为0
1:不产生效果
void CPIO_Setbits(GPIO_TypeDef*GPIOx,uint16_t GPIO_PIN)
{
GPIOB->BSR |=GPIO_PIN;
}
在.c文件里写的函数,别忘记去对应的.h文件里添加声明
定义初始化寄存器结构体
前面的课程中点亮LED需要先给时钟使能,然后设置输出模式,这两部都需要查询手册才知道,可移植性不高,初始化寄存器结构体的目的就是,将使能和设置输出模式这两部放到初始化中
strucr{
GPIO_PIN_X//选择设置的引脚(Px0是0x0001,Px1是0x0002,Px5是0x0020)
GPIO_Speed//CRL中的速度
GPIO_Mode//输入输出模式
}GBPO_InitTypedef
错误总结:1.定义结构体的名字是typedef struct,不是只有struct
2.结构体内部成员变量别忘了写数据类型,此数据类型不需要加括号
3.别打错字
正确写法
Mode和Speed采用enum方式声明,枚举定义enum,enum里面是逗号,配置第一个值后,后面值会自动+1
为什么用枚举?
速度是uint16_t,也就是16位的,取值范围有65536(2^16)这么多,而CRL里的速度只有2,10,50三种,为了防止犯错,又没有必要写成十六进制,枚举就解决了这个问题,只在枚举的范围内取值,并且配置第一个值后,后面值会自动+1。
以下两种命名方式都可以,但是库艰苦采用了右边的方式,为了方便以后使用,所以和固件库同名
模式
其实没有必要弄明白固件库的每一位是如何对应CNF[1:0]和MODE[1:0],只要会用就行,只要后面弄懂自己写的库的逻辑就好
bit0和bit1对应速度,速度前面单独定义过了,所以这里不写,到时候会加上速度
bit3,bit2对应了中文参考手册中的MODE位
bit4是输入输出设置位,0输入,1输出
上拉输入和下拉输入除了要看bit6,bit5,还需要BSRR和BRR寄存器的参与
下拉输是接地,所以BRR中PBx位要置一;上拉是接高电平,所以BSRR中PBx位要置一
GPIO_Init
这里笔者就分析一下低八位的代码
在分析说一下,GPIO一共有16个io口,CRL控制Px0-Px7,剩下八个由CRH寄存器控制
我们假设选择推挽输出模式,对应的GPIO_MODE就是0x10,速度选择0x01,IO口选择PB1
以下分析的时候,没有0x都是二进制
line 79:currentmode=0001 0000 & 0000 1111=0000 0000;
line 83:if(0001 0000 & 0001 0000) 结果不等于0000 0000,所以到
line 86:选择速度GPIO_Speed=0000 0001;
currentmode |=理解为 currentmode= currentmode | GPIO_Speed;
也就是currentmode=0000 0000 | 0000 0001=0000 0001;到这里,速度模式就选好了
插一嘴,如果不明白line 86,可以查一下C语言里的这些运算 |= ,*=,+=, &=,都是一个意思,需要我写的话,可以留言
由于前面我们已经选好了CRL的速度和模式,现在还差PB1没有选,下面就进入IO口的选择
line 93:先将配置好的速度模式存入temreg(temp register)=0001
这一步需要注意的是,其实CRL寄存器的是没有办法每一位都和GPIO_MODE对应上的,通过上述判断编译器已经确定了是推挽输出,并且速度是10MHz,也就是说,CRL目前的值就是0001
接下来就是判断到底Px几是0001了
line 96: 进入for循环 pinpos起始值是0000 0000,且小于0000 1000
line 99:pos=0000 0001左移pinpos(pinpos此时为零,相当于没有移动)=0000 0001
line 102: currentpin=00000000 00000010 & 00000000 00000001=0x0000
line 105: currentpin是0x0000,pos是0x0001,二者明显不相等,所以不进if,直接到ine 122的else
line 125: 我们选择的GPIO_MODE是0x10,不等于0x48,所以不进line 128的if
直接到line 134,目前CRL寄存器里的值就是temreg,然后再次进入for循环,此时pinpos=0x01
line 99:pos=0000 0001左移0000 0001,也就是pos=00000010;
line102:currentpin=00000000 00000010 & 00000000 00000010=0x0002
line105: 此时currentpin=pos都为00000000 00000010;进入if
line108:pos=0000 0001<<2=0000 0100(4b,二进制的值是4)
line110: pinmask=0000 1111<<4=1111 0000
对于line111行,PB1的话清空的是CRL里的4,5,6,7四位,手册里有写,复位值是0x4444 4444
也就是复位值是八个0100,所以没有选的引脚都是0100,
如果是PB3的话就是0001 0100 0100 & 0000 1111 1111
line111: tmpreg & 0000 1111=0001 0100 & 0000 1111=0000 0100
line114:tmpreg=tmpreg | (0000 0001<<4)=tmpreg | 0001 0000
=0001 0100 | 0001 0000=0001 0100
line117:我们选择了开漏,肯定不是GPIO_ODE_IPD,所以不近if,直接到134
最终输出模式就是0100 0100 0100 0100 0100 0100 0001 0100,也就是PB1是0001
至此低八位就分析完了,高八位同样,因为我自己分析了第八位,所以高八位我也理解,就不再此分析了,有需要的话留言,我在补上。
再加一句,在看这段代码的时候,低八位IO口由CRL寄存器控制,高八位由CRH控制,可见一共有16个IO,但是我在指南者板子上只看到了12个,这是因为,能够开放给我们使用的是12个,像一些电源比如3V,5V,GND的引脚已经封装好了。
这部分我犯得错误
首先是初始化不写初始化函数、GPIO_Init让你吃了??
有初始化函数了还需要打开时钟,别忘了
最后就是视频里提到的,定义变量必须在{之后,不然的话会报错