特别有意思,LED的灯序与其硬件地址刚好相反,没办法直接通过加1实现二进制进位的亮灯操作,查了一些资料说用数组和switch实现,觉得太麻烦了,思索良久,就想到了反转二进制数解决这个问题。
reverse_bits( )是实现反转二进制数:
/* 位反转函数
@param num 待反转的8位数据
@return 返回位顺序反转后的数值 */
unsigned char reverse_bits(unsigned char num) {
unsigned char reversed = 0;
int i;
for (i = 0; i < 8; i++) { // 遍历每一位
reversed |= ((num >> i) & 1) << (7 - i); // 将第i位移至对称位置
}
return reversed; // 例如:0b00000001 -> 0b10000000
}
说明:
>> 将操作数的所有位向右移动指定的位数。
<< 将操作数的所有位向左移动指定的位数。
& 对两个操作数的每一位执行逻辑与操作,如果两个相应的位都为 1,则结果为 1,否则为 0。(全1则1)
| 对两个操作数的每一位执行逻辑或操作,如果两个相应的位都为 0,则结果为0 ,否则为 1。(有1则1)
以下代码是通过按键实现二进制进位的亮灯操作(STC89C52RC):
#include <REGX52.H> // 包含8052单片机寄存器定义头文件
#include <INTRINS.H> // 包含内联函数库(如_nop_())
/* 延时函数
@param num 延时次数,次数越多总延时越长
@11.0592MHz晶振下,每次循环约1ms */
void Delay(int num) //@11.0592MHz
{
while(num){
unsigned char i, j;
_nop_(); // 插入一个空指令周期(1.085us)
i = 2;
j = 199;
do { // 双重循环实现精确延时
while (--j); // 内层循环约0.5ms
} while (--i); // 外层循环2次,总延时约1ms
num--; // 控制总延时次数
}
}
/* 位反转函数
@param num 待反转的8位数据
@return 返回位顺序反转后的数值 */
unsigned char reverse_bits(unsigned char num) {
unsigned char reversed = 0;
int i;
for (i = 0; i < 8; i++) { // 遍历每一位
reversed |= ((num >> i) & 1) << (7 - i); // 将第i位移至对称位置
}
return reversed; // 例如:0b00000001 -> 0b10000000
}
/* 主函数:检测按键控制LED显示 */
void main() {
//8051/52 系列单片机的寄存器是 8 位
// 代码中 P2 端口驱动 8 个 LED,每个引脚对应 1 个 LED
//通过 char 类型的位操作可以直接控制每个 LED 的状态:
unsigned char num=0;
while(1) { // 主循环
if (P3_1 == 0) { // 检测P3.1引脚(如按键)是否按下(低电平)
Delay(20); // 延时20ms消抖
while (P3_1 == 0); // 等待按键释放(保持阻塞直到松开)
Delay(20); // 再次消抖
num++;
P2=~(reverse_bits(num)); // 数值取反后发送到P2口驱动LED
/* 执行流程:
1. reverse_bits(num):将数值位反转
2. ~:按位取反(因LED通常低电平点亮)
示例:num=1(0x01)
→ 反转后0x80
→ 取反0x7F → P2.7引脚低电平点亮LED */
}
}
}
效果展示: