单片机位操作
特殊功能寄存器声明:sfr
如声明P0
的寄存器,就将P0
的地址赋值给sfr
类型的P0
变量上。修改P0
的值,就等于修改P0
指向的寄存器的值。这些寄存器是硬件中的一部分,用于控制和监视芯片的各种功能。修改这些寄存器的值,可以影响硬件电路的状态和行为,因为这些寄存器直接与外部设备、引脚和功能相连。
特殊位声明:sbit
声明P0寄存器的第一位,可以用sbit P01 = P0^1
,也可以用sbit P01=0x81
。
对P01
赋值,可以直接修改P0
寄存器的第1
位。
位从0开始,如果要对第0位赋值,那就是sbit P00=P0^0
或者sbit P00=0x80
虽然值都是0x80
,但sfr
声明的P0
变量会修改0x80
指向的8位寄存器中的每一位,sbit
声明的P00
只会修改0x80
这一位的值。
不可位寻址
单片机无法对所有位进行编码,如果要单独对寄存器的某一位进行赋值,并且不影响其他位,可以用&=
、|=
、^=
的方法。
&=
:用于对某一位置0:11111110
|=
:用于对某一位置1:00000001
^=
:用于对某一位取反:对00000000
异或后对00000001
异或。
位选与段选
对于LED点阵,单片没有那么多资源来直接控制每一个IO口。
我们可以先选择某一行,称为段选。再对这一行上的每一列设定状态,也就是对每一位指定高低电平,称为位选。如此选择下一行,控制下一行的每一列的状态,如果速度够快,人眼看到的就是一幅图像。
上面的过程为逐行扫描,也可以逐列扫描。逐列扫描就是先选择某一列,然后控制这一列的每一行的状态。如此循环。
原理图中的DP的值是由74HC595(串转并)模块控制的,如果要通过逐行扫描的方式点亮LED点阵,我们需要先选择某一行,为这一行指定高电平。选择高电平是因为DP与LED的高电平一端相连。
74HC595(串转并)模块
输入端我们所能控制的有三个:
P35
-RCLK
,R
代表 “Register
”,用于控制存储寄存器(Register
)的时钟输入。P36
-SRCLK
,SR
代表 “Shift Register
”,用于控制移位寄存器(Shift Register
)的时钟输入。P34
-SER
,SER
代表"Serial Input
",意为串行输入。
工作流程为:
- 通过
SER
将数据的最高位也就是第7
位传入,将数据<<1
,使得第6
位成为新的最高位。 - 将
SRCLK
置0
再置1
。 - 重复以上两步,直到
8
位输入全部输入。 - 将
RCLK
置0
再置1
。此时74HC595
的输出即为预定的输出。
置0再置1的原因是因为在74HC595芯片中,数据的存入是在上升沿时发生的。
void hc595_write_data(u8 dat)
{
u8 i=0;
for(i=0;i<8;i++)
{
HC595_SER=dat>>7;
dat<<=1;
HC595_SRCLK=0;
delay_10us(1);
HC595_SRCLK=1;
}
HC595_RCLK=0;
delay_10us(1);
HC595_RCLK=1;
}
如果不添加延时,那么LED点阵的内容将是随机的。
位选
在通过74hc595选定要点亮的列后,通过修改P0寄存器的值,就可以修改这一行上,各列的显示状态。
hc595_write_data(0x80);
P0=0xaa;
上面的代码会让第一行隔一个灯亮灭一次。
由于原理图上,P0连接的是二极管的低电平一端,所以第一行的第一个灯是灭的。
文字、图片、动画
要让LED点阵显示图片和文字,需要得到文字和图片对应的点阵,然后逐行扫描出来。
void draw(u8 rows[]){
u8 i=0;
for(;i<8;i++){
hc595_write_data(1<<i);
P0=~rows[i];
delay_10us(1);
hc595_write_data(0);
P0=0xff;
}
}
由于上面的代码中延迟很低,帧率比较高,那么函数结尾就需要将P0和hc595置为不点亮灯泡的状态,否则会出现残影。
由于我们封装了绘制图片的方法,那么如果实现一个动画,就可以多次调用该方法,将每一帧的内容打印出来。
(delay_10us延时是有问题的,图省事我就不改了)