前言
相比于库函数,有过单片机基础的对于寄存器写法肯定更熟悉,写起来也会更简单,正点原子的课程过于坑爹,那么在正点原子课程中没有细说的地方我会做出补充,当然也要配合资料来看,正点原子的资料还是很棒的,有过单片机学习经验的看懂不是很难
思路
依旧是老规矩,先分析思路,无论写什么代码都要知道自己要做什么
对于点亮LED来说
第一步:使能外部时钟 APB2总线下的外部时钟
第二部:初始化引脚 PA8,PD2,50HZ推挽输出
第三步:点灯 置1,延时,置0
具体的细节在上一篇库函数已经交代过,这里不过多赘述
创建文件
首先创建一个led.h文件,用来防止复用,上一篇库函数已经讲过,直接看代码
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
#endif
创建C文件
也就是初始化函数的文件
根据上文说过的寄存器第一步就是使能外部时钟
我手中的板子led是PA8和PD2
这是配置的代码
RCC->APB2ENR|=1<<2;
RCC->APB2ENR|=1<<5;
按照对应的寄存器设置,A位在第二位,所以将1向左移动两位,D位同理
至于为何用左移右移的方式,因为对于复杂的单片机而言,像常规的设置整个寄存器的状态可能会对其他寄存器造成影响,使用这种方法就可以避免影响到寄存器的其他位
|=符号则是C语言中的或运算,学过数电的都知道,1与任何数或都为1 所以在操纵寄存器单个数据的时候我们一般置1使用XXX|=1<<2;置0一般使用XXX&=~(1<<2);的方法
如果确定其他位的状态,假如只有第三位为1,其他为0,就可以写XXX=0x04;这样的十六进制写法
RCC是关与寄存器的一个结构体变量缩写名称,APB2ENR是成员变量,所以用指向的方式写,意思是外设时钟使能寄存器
初始化引脚
如图所示,引脚有十六个,分为高八位和低八位,0-7属于低八位,8-15为高八位,每位引脚由四位数据控制状态,而控制引脚的四位数据中,前两位控制输入输出,一旦选定,后两位控制输入输出的状态,所以后两位看起来同样的数据配置有两种状态,其实是由输入输出决定的
我们的引脚为PD2和PA8,所以选低八位的第3组的四位数据和高八位的第一组的数据
写法是
GPIOA->CRH&=0xfffffff0;
GPIOA->CRH|=0x00000003;
GPIOD->CRL&=0xfffff0ff;
GPIOD->CRL|=0x00000300;
第一行和第三行是为了清除目标为的状态并置零
寄存器名称如图所示不再多说
看图上的清除置零方法,上面提到,置零要用与非的方式,可这里是与运算,因为十六进制的F转换为二进制就是1111,对应四位控制位,而1与任何数与都等于任何数,0与任何数与都等于0,所以用这种方式既可以置零,又不会影响其他位的状态
2,4行置1上文已经解释过,之说一下3是什么意思,十六进制的3转换为2进制就是0011对应图中的50HZ推挽输出模式
最后设置初始状态
如图可见16位数据置1,0就可以控制所以直接写就好,没得讲
GPIOD->ODR|=1<<2;
GPIOA->ODR|=1<<8;
完整代码为
#include "led.h"
#include "stm32f10x.h"
void LED_Init()
{
RCC->APB2ENR|=1<<2; //启用GPIOA的时钟 APB2ENR使能/禁用APB2时钟寄存器 ENG就是使能/禁用
RCC->APB2ENR|=1<<5;//启用GPIOD的时钟 ||至于-》的写法,是因为APB2ENG是RCC结构体的成员变量
//CR就是配置寄存器的意思
GPIOA->CRH&=0xfffffff0;//清除A8位置0
GPIOA->CRH|=0x00000003;//设为推挽输出
//mini板有0-15个pin口,分别对应,CRH和CRL各32位,每四位控制一个引脚,各控制八个引脚
//即外设IO口A的高配置寄存器的0-3位也就是8号引脚
GPIOA->ODR|=1<<8;//PA8输出高电平
GPIOD->CRL&=0xfffff0ff;//清除置0
GPIOD->CRL|=0x00000300;
GPIOD->ODR|=1<<2;
//即外设IO口D的低配置寄存器的8-11位也就是2号引脚
}
main函数
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
int main(void)
{
delay_init(72);
LED_Init();
while(1)
{
GPIOA->ODR|=1<<8;
GPIOD->ODR|=1<<2;
delay_ms(50);
GPIOA->ODR&=~(1<<8);
GPIOD->ODR&=~(1<<2);
delay_ms(50);
}
}
其中的代码在上篇库函数和这一篇都已经解释过
唯一不同就是delay初始化不知道为什么突然要初始值了