源码基本STM32F103C8T6 MCP2518FD开发板 MCP2518FD_CANFD开发板
习惯了PIC单片机的IO设置,就一个TRISx/PORTx/LATx 三个寄存器就搞定了,但应用到STM32时,调试ds18b20时,要设置IO读了ds18b20的数据,开始就有点蒙,看了规格书,感觉还是有点摸不着头脑,所以狂查资料,并写个笔记。
//IO方向设置
#define DS18B20_IO_IN() {GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;}
#define DS18B20_IO_OUT() {GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=3<<0;}
现在我要定义到PB6引脚,应该怎么定义呢?
规格书里有说明,STM32 的 IO 口可以通过软件配置成如下 8 种模式:
- 1 输入浮空
- 2 输入上拉
- 3 输入下拉
- 4 模拟输入
- 5 开漏输出
- 6 推挽输出
- 7 推挽式复用功能
- 8 开漏复用功能
配置模式由2 个 32 位的端口配置寄存器 CRL 和 CRH,
2 个 32 位的数据寄存器 IDR 和 ODR;
1 个 32 位的置位/复位寄存器BSRR;
一个 16 位的复位寄存器 BRR;
1 个 32 位的锁存寄存器 LCKR;
我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR。CRL 和 CRH 控制着每个 IO 口的模式及输出速率。
STM32 的 IO 口位配置表如表所示:
STM32 输出模式配置如表 :
下面看端口低配置寄存器 CRL (以PA为例,CRL控制PA0---PA7,CRH控制PA8---PA15)的描述 :
STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。每个 IO 端口占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。
所以如果我们要控制PA0,可以这样子写:
GPIOA->CRL&=0XFFFFFFF0;
//GPIOA->CRL=GPIOA->CRL & 0b1111 1111 1111 1111 1111 1111 1111 0000;
把PA0设置为输入:
GPIOA->CRL|=8<<0; // 上拉/下拉输入模式
合起来控制写法:
GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;
所以,才有上面的写法:
#define DS18B20_IO_IN() {GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;}
PA0为输入(上/下拉)
GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;
PA0为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=3<<0;
PA1为输入(上/下拉)
GPIOA->CRL&=0XFFFFFF0F;GPIOA->CRL|=8<<4;
PA1为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFFFFFF0F;GPIOA->CRL|=3<<4;
PA2为输入(上/下拉)
GPIOA->CRL&=0XFFFFF0FF;GPIOA->CRL|=8<<8;
PA2为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFFFFF0FF;GPIOA->CRL|=3<<8;
PA3为输入(上/下拉)
GPIOA->CRL&=0XFFFF0FFF;GPIOA->CRL|=8<<12;
PA3为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFFFF0FFF;GPIOA->CRL|=3<<12;
PA4为输入(上/下拉)
GPIOA->CRL&=0XFFF0FFFF;GPIOA->CRL|=8<<16;
PA4为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFFF0FFFF;GPIOA->CRL|=3<<16;
PA5为输入(上/下拉)
GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=8<<20;
PA5为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=3<<20;
PA6为输入(上/下拉)
GPIOA->CRL&=0XF0FFFFFF;GPIOA->CRL|=8<<24;
PA6为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XF0FFFFFF;GPIOA->CRL|=3<<24;
PA7为输入(上/下拉)
GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=8<<28;
PA7为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=3<<28;
下面是CRH寄存器器:
和CRL同样的写法,只不过CRH管理是PA8---PA15 端口。
PA8为输入(上/下拉)
GPIOA->CRH&=0XFFFFFFF0;GPIOA->CRH|=8<<0;
PA8为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XFFFFFF0F;GPIOA->CRH|=3<<0;
PA9为输入(上/下拉)
GPIOA->CRH&=0XFFFFFF0F;GPIOA->CRH|=8<<4;
PA9为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XFFFFFF0F;GPIOA->CRH|=3<<4;
PA10为输入(上/下拉)
GPIOA->CRH&=0XFFFFF0FF;GPIOA->CRH|=8<<8;
PA10为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XFFFFF0FF;GPIOA->CRH|=3<<8;
PA11为输入(上/下拉)
GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=8<<12;
PA11为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=3<<12;
PA12为输入(上/下拉)
GPIOA->CRH&=0XFFF0FFFF;GPIOA->CRH|=8<<16;
PA12为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XFFF0FFFF;GPIOA->CRH|=3<<16;
PA13为输入(上/下拉)
GPIOA->CRH&=0XFF0FFFFF;GPIOA->CRH|=8<<20;
PA13为输出(通用推挽输出50MHZ)
GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRH|=3<<20;
PA14为输入(上/下拉)
GPIOA->CRH&=0XF0FFFFFF;GPIOA->CRH|=8<<24;
PA14为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0XF0FFFFFF;GPIOA->CRH|=3<<24;
PA15为输入(上/下拉)
GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=8<<28;
PA15为输出(通用推挽输出50MHZ)
GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=3<<28;
如果是对端口C操作,方法同理,只是寄存器要找成GPIOC
GPIOC->CRL&=0XFFFFFFF0;
GPIOx->CRL,这句话表示要操作GPIOx的低8位,就是Px0 ~ Px7
GPIOx->CRH,这句话表示要操作GPIOx的高8位,就是Px8 ~ Px15
所以GPIOC->CRL,这句话表示要操作GPIOC,
后面的0XFFFFFFF0,表示操作PC0;
0XFFFFFF0F,表示操作PC1;
0XFFFFF0FF,表示操作PC2;
0XFFFF0FFF,表示操作PC3;
0XFFF0FFFF,表示操作PC4;
0XFF0FFFFF,表示操作PC5;
0XF0FFFFFF,表示操作PC3;
0X0FFFFFFF,表示操作PC7;
合起来的意思就是:利用“与”运算,把这个位清0,同时不影响其他的位的设置。
GPIOC->CRL|=8<<0;
意思就是将1000左移0位(不移位),然后再与GPIOC->CRL进行“或”运算。
再根据原子的寄存器开发手册可以知道CNF0[10]、MODEO[00],对应的就是设置为上拉/输入模式。
如果要将ds18B20 data引脚分配成 PB6,写法如下:
#define DS18B20_IO_IN() {GPIOB->CRL&=0XF0FFFFFF;GPIOA->CRL|=8<<24;}
#define DS18B20_IO_OUT() {GPIOB->CRL&=0XF0FFFFFF;GPIOA->CRL|=3<<24;}