之前看的库函数操作的LED灯,和平常调用读卡器无异。这次读下芯片文档,用空工程模板操作寄存器控制LED灯。
第一次接触寄存器操作,有点烧脑,烧脑过后感觉比库函数操作还清晰些。APB2ENR,CRL,ODR寄存器,很巧妙,也很符合物理常识。
我这个是STM32 Nano开发板
首先查LED对应的引脚,LED对应的是PC0-PC7,即GPIO的0-7引脚
然后查外设时钟寄存器,GPIOC对应的是APB2ENR的第4位,把这个位置设置为1即开启了GPIOC的时钟。
然后查CRL寄存器文档(端口配置寄存器),LED的端口是0-7对应的低位寄存器CRL,如果是8-15对应CRH寄存器,这里用32位描述8个端口模式。每4位描述一个端口。比如0-3位描述GPIOC-0的输出模式。其中4位的低两位描述输出模式:输出速率。高两位描述推挽输出这些模式。
然后查询ODR寄存器文档(端口输出数据寄存器),他的32位高16位保留。第16位对应一组GPIO的16个端口。响应位置为1,则响应端口高电平,响应位置为0则响应端口低电平。
有以上资料的话就可以通过寄存器实现跑马灯了。
1.收到得到板子LED0-7对应的是GPIOC的时钟
2.通过寄存器设置GPIOC时钟对应的位置为1,让时钟开启
3.由于LED在0-7端口,所以通过CRL寄存器设置每个端口的输出模式。LED采用推挽输出模式。
4.通过控制ODR寄存器的响应端口位置的值控制电平高低
代码用到了位移、与、或、非运行,目的都是操作寄存器特定位置的值。
led.h
//定义宏,防止头文件重复引用
#ifndef __LED
#define __LED
//初始化LED的环境
void InitLedEnv(void);
#endif
led.c
#include "led.h"
#include "sys.h"
//初始化LED环境
void InitLedEnv(void)
{
//设置ABP2外设时钟第4位位1,即可用,对应GPIOC时钟
//1<<4:1左移4位位1000,与APB2ENR寄存器值或运算就是第4位设置1,其他不变的意思
RCC->APB2ENR|=1<<4;
//设置LED0为推挽输出模式
//CRL寄存器和十六进制的值且运行,设置指定位置的电平为指定4位位0000
GPIOC->CRL&=0XFFFFFFF0;
//CRL寄存器和十六进制的值或运算,设置指定位置的电平为指定4位位0011,四位里面高两位00表示通用推挽输出。低两位11表示输出模式,最大速度50MHz
GPIOC->CRL|=0X00000003;
//依次设置其他7个LED输出空为推挽输出模式
//LED1
GPIOC->CRL&=0XFFFFFF0F;
GPIOC->CRL|=0X00000030;
//LED2
GPIOC->CRL&=0XFFFFF0FF;
GPIOC->CRL|=0X00000300;
//LED3
GPIOC->CRL&=0XFFFF0FFF;
GPIOC->CRL|=0X00003000;
//LED4
GPIOC->CRL&=0XFFF0FFFF;
GPIOC->CRL|=0X00030000;
//LED5
GPIOC->CRL&=0XFF0FFFFF;
GPIOC->CRL|=0X00300000;
//LED6
GPIOC->CRL&=0XF0FFFFFF;
GPIOC->CRL|=0X03000000;
//LED7
GPIOC->CRL&=0X0FFFFFFF;
GPIOC->CRL|=0X30000000;
//通过操作ODR寄存器设置每个LED位置为1即高电平,不亮
GPIOC->ODR|=1<<0;
GPIOC->ODR|=1<<1;
GPIOC->ODR|=1<<2;
GPIOC->ODR|=1<<3;
GPIOC->ODR|=1<<4;
GPIOC->ODR|=1<<5;
GPIOC->ODR|=1<<6;
GPIOC->ODR|=1<<7;
}
mian.c
#include "led.h"
#include "delay.h"
//zlz第一个寄存器操作的LED灯
int main(void)
{
//初始化时钟
Stm32_Clock_Init(9);
//初始化延时函数
delay_init(72);
//初始化LED灯
InitLedEnv();
//当前该点亮的LED索引
int curIndex=0;
//死循环
while(1)
{
//改版为点亮所以led做跑马灯
for(int i=0;i<8;i++)
{
//当前该亮的灯点亮
if(i==curIndex)
{
//1左移当前索引位数后取非再做且运算就是把当前位数的电平设置低位。比如i=2即LED2,00000100取非之后为11111011再和ODR的值与运算
//第3位电平设置低,其他不变
GPIOC->ODR&=~(1<<i);
}
//其他熄灭
else
{
//1左移当前索引位数再做或运算就是把当前位数的电平设置高位。比如i=2即LED2,ODR寄存器值和00000100或运算
//第3位电平设置高,其他不变
GPIOC->ODR|=1<<i;
}
}
//下次点亮下一个灯
curIndex++;
//8个灯取余数
curIndex=curIndex%8;
//延迟半秒
delay_ms(500);
}
}
编译后下载到板子运行的效果就是8个灯轮流亮的跑马灯效果。