不设计硬件,假定有一40个灯的控制系统,设计一子函数实现其中任一灯的亮、灭切换。
(怎么用一个八位数的五个单元数组去控制)
你可以使用一个八位数的五个单元数组来控制这40个灯,每个单元包含八个二进制位,可以控制8盏灯。以下是实现其中任一灯的亮、灭切换的示例代码:
#include <stdio.h>
// 定义一个八位数的五个单元数组控制40个灯
unsigned char lamp[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
// 定义一个函数实现其中一盏灯的亮灭切换
void switch_lamp(int n, int state) {
// 计算该灯所在单元和位
int unit = (n - 1) / 8;
int bit = (n - 1) % 8;
// 设置或清除该灯的状态
if (state == 1) {
lamp[unit] |= (1 << bit);
} else {
lamp[unit] &= ~(1 << bit);
}
}
int main() {
int n = 15; // 假设要控制第15盏灯
printf("初始状态:0x%02x%02x%02x%02x%02x\n", lamp[4], lamp[3], lamp[2], lamp[1], lamp[0]);
switch_lamp(n, 1); // 打开第15盏灯
printf("打开第%d盏灯后状态:0x%02x%02x%02x%02x%02x\n", n, lamp[4], lamp[3], lamp[2], lamp[1], lamp[0]);
switch_lamp(n, 0); // 关闭第15盏灯
printf("关闭第%d盏灯后状态:0x%02x%02x%02x%02x%02x\n", n, lamp[4], lamp[3], lamp[2], lamp[1], lamp[0]);
return 0;
}
在这个示例代码中,使用一个八位数的五个单元数组来控制40个灯,其中每个单元包含八个二进制位,可以控制8盏灯。通过 switch_lamp 函数可以实现其中一盏灯的亮灭切换,该函数的参数 n 表示要控制的灯的编号,state 表示要设置的状态。如果 state 为 1,则表示将该灯打开,否则将该灯关闭。在 main 函数中测试了实现。
现在可以回到正文开头的代码中了。这个10us的函数是怎么得出来的呢?
这个我之前查过很多资料,比如执行while语句需要多少个机器周期。赋值需要多少个周期。也就是查这个占用了我很大一部分时间。直到最后将上面的延时函数直接调到main函数中debug调试,才明白,问题其实很简单啊。
无论是执行什么语句,最终都会回到汇编上来,debug里单步调试,所有的指令周期就会明明白白了。
我用main函数直接调用延时函数,如下:
void Delay10us() //@12.000MHz
{
unsigned char i;
_nop_();
_nop_();
i = 27;
while (--i);
}
main
{
Delay10us();
}
我用的keil软件,将上述build之后,点击debug,开始调试
.
看图片上,开始debug,程序的起始就在C:0x0183 020171 LJMP Delay10us(C:0171),
这里有个长转移指令LJMP,它要转移到C:0171行去执行Delay10us这个函数。
那执行LJMP这个指令需要多长时间呢,查找STC数据手册,在1T模式下,此条指令在单片机上运行需要4个时钟周期。
接下来,按单步调试F11键,如下图:
程序成功转移到C:0171行,跳转到Delay10us函数中,此行程序执行NOP指令,空操作。查STC数据手册,NOP指令占用1个时钟周期。
接下来C:0172行,依然是NOP指令,1个时钟周期。
接下来C:0173行,此行执行 MOV R7,#0x1B,将立即数送入寄存器。是将27赋值给i。依然查手册,此条指令2个时钟周期。
继续:
此时执行到while语句了,这里执行的指令时 DJNZ R7,C:0175,寄存器减1非0转移。此条指令执行1次4个时钟周期。
上面已经将寄存器填入27了,因此这条指令将执行27次。
继续:
循环了27次,终于到0了,程序继续向下执行,此行指令RET,子程序返回。此条指令4个时钟周期。
继续:
程序又回到了起点。
好了,可以计算一下此次延时的时间了。1个LJMP,4时钟;2个NOP,2时钟;1个MOV,2时钟;27个DJNZ,108时钟;1个RET,4时钟。
4+2+2+108+4=120。
单片机的时钟周期是:1(S)/12MHz = 1/12(us)
此次延时的时间是:120 × 1/12(us)= 10(us)