STC8H8K64U 学习笔记 -位运算
- 环境说明
- 引脚说明
- 位运算
- 实例
环境说明
该内容仅针对我自己学习的开发板做的笔记,在实际开发中需要针对目标电路板的原理图进行针对性研究。
- 芯片:STC8H8K64U
- 烧录软件:stc-isp-v6.92G
- 编码工具:天问
引脚说明
P0_0
:蜂鸣器,按频率发音,1
:高,0
:低P0_1
:电动马达,0
:停,1
:动P5_3
:小蓝灯,0
:灭,1
:亮P2_7
:1 号 LED 灯,0
:亮,1
:灭P2_6
:2 号 LED 灯,0
:亮,1
:灭P1_5
:3 号 LED 灯,0
:亮,1
:灭P1_4
:4 号 LED 灯,0
:亮,1
:灭P2_3
:5 号 LED 灯,0
:亮,1
:灭P2_2
:6 号 LED 灯,0
:亮,1
:灭P2_1
:7 号 LED 灯,0
:亮,1
:灭P2_0
:8 号 LED 灯,0
:亮,1
:灭P5_1
:1 号按键,0
:按下,1
:弹起P5_2
:2 号按键,0
:按下,1
:弹起P5_3
:3 号按键,0
:按下,1
:弹起P5_4
:4 号按键,0
:按下,1
:弹起P3_4
:矩阵键盘第 1 行引脚P3_5
:矩阵键盘第 2 行引脚P4_0
:矩阵键盘第 3 行引脚P4_1
:矩阵键盘第 4 行引脚P0_3
:矩阵键盘第 1 列引脚P0_6
:矩阵键盘第 2 列引脚P0_7
:矩阵键盘第 3 列引脚P1_7
:矩阵键盘第 4 列引脚
位运算
对位运算进行小结
分析:设 n 为二进制数值的某一个位,在 8 位寄存器中,从高到低依次是:7654 3210
如果希望通过一个变量的二进制位置来作为按钮是否按下的标志位,
uint8 pressed = 0x00;
// 变量 pressed 的低四位表示按键的按下状态, 0 表示未按下, 1表示已按下
/ 7 6 5 4 3 2 1 0 --> n 的取值
// 0 0 0 0 0 0 0 0 --> 二进制值
// K4 K3 K2 K1 --> 针对按钮
若需要检测 n = 0 所在的位置的值是零还是壹,可以将 pressed
取反后与 0x01
进行与运算,如下所示
Let's say pressed is 0000 1010~pressed is 1111 01010x01 is 0000 0001 &The result is 0000 0001Let's say pressed is 0000 1011~pressed is 1111 01000x01 is 0000 0001 &The result is 0000 0000
若需要检测 n = 1 所在的位置的值是零还是壹,可以将 pressed
取反后与 0x02
进行与运算,如下所示
Let's say pressed is 0000 1000~pressed is 1111 01110x02 is 0000 0010 &The result is 0000 0010Let's say pressed is 0000 1011~pressed is 1111 01000x01 is 0000 0010 &The result is 0000 0000
类推:
- 若检测第
1
位的值,即n=0
,将pressed
取反后与0x01
进行与运算- 若运算结果为
0x01
, 则判定该位的值为0
- 若运算结果为
0x00
, 则判定该位的值为1
- 若运算结果为
- 若检测第
2
位的值,即n=1
,将pressed
取反后与0x02
进行与运算- 若运算结果为
0x02
, 则判定该位的值为0
- 若运算结果为
0x00
, 则判定该位的值为1
- 若运算结果为
- 若检测第
3
位的值,即n=2
,将pressed
取反后与0x04
进行与运算- 若运算结果为
0x04
, 则判定该位的值为0
- 若运算结果为
0x00
, 则判定该位的值为1
- 若运算结果为
- 若检测第
4
位的值,即n=3
,将pressed
取反后与0x08
进行与运算- 若运算结果为
0x08
, 则判定该位的值为0
- 若运算结果为
0x00
, 则判定该位的值为1
- 若运算结果为
不难发现,进行与运算的对象和 n 是有一定的关系的:
1<<0=0x1
1<<1=0x2
1<<2=0x4
1<<3=0x8
1<<4=0x10
1<<5=0x20
1<<6=0x40
1<<7=0x80
综上所述,总结如下:
设 n 为二进制数值的某一个位,在 8 位寄存器中,从高到低依次是:7654 3210
希望通过一个变量
pressed
的二进制位置来作为按钮是否按下的标志位,有如下结论:若检测
n
位置的的值,将pressed
取反后与1<<n
进行&
运算
- 若运算结果为
1<<n
, 则判定该位的值为0
- 若运算结果为
0x00
, 则判定该位的值为1
实例
下面是一段来自天问编程工具中的代码。
针对的是开发板上的四个独立按键,它们对应的四个引脚分别是:P5_1、P5_2、P5_3 和 P5_4。
需求是,当按下按键后,向串口发送信息,报告此时按键的状态,并用一个标志位来记录其按下的状态。
#include <STC8HX.h>
uint32 sys_clk = 24000000;//设置PWM、定时器、串口、EEPROM频率参数
#include "lib/twen_board.h"
#include "lib/delay.h"
#include "lib/UART.h"
#include <stdio.h>#define K1 P5_1
#define K2 P5_2
#define K3 P5_3
#define K4 P5_4uint8 pressed = 0x00;
// 变量 pressed 的低四位表示按键的按下状态, 0 表示未按下, 1表示已按下
// 0 0 0 0 0 0 0 0
// K4 K3 K2 K1void setup() {twen_board_init(); //天问51初始化uart_init(UART_1, UART1_RX_P30, UART1_TX_P31, 115200, TIM_1);//初始化串口P5M1&=~0x02; P5M0&=~0x02; // P5_1 设置为双向IOP5M1&=~0x04; P5M0&=~0x04; // P5_2 设置为双向IOP5M1&=~0x08; P5M0&=~0x08; // P5_3 设置为双向IOP5M1&=~0x80; P5M0&=~0x80; // P5_4 设置为双向IO
}void loop() {// 0000 0001if(K1 == 0 && ((0x01 & ~pressed) == 0x01)) {pressed|=0x01;uart_putstr(UART_1, "Key1 按下");} else if(K1 == 1 && ((0x01 &~ pressed) == 0x00)) {pressed&=~0x01;uart_putstr(UART_1, "Key1 弹起");}// 0000 0010if(K2 == 0 && ((0x02 & ~pressed) == 0x02)) {pressed|=0x02;uart_putstr(UART_1, "Key2 按下");} else if(K2 == 1 && ((0x02 &~ pressed) == 0x00)) {pressed&=~0x02;uart_putstr(UART_1, "Key2 弹起");}// 0000 0100if(K3 == 0 && ((0x04 & ~pressed) == 0x04)) {pressed|=0x04;uart_putstr(UART_1, "Key3 按下");} else if(K3 == 1 && ((0x04 &~ pressed) == 0x00)) {pressed&=~0x04;uart_putstr(UART_1, "Key3 弹起");}// 0000 1000if(K4 == 0 && ((0x08 & ~pressed) == 0x08)) {pressed|=0x08;uart_putstr(UART_1, "Key4 按下");} else if(K4 == 1 && ((0x08 &~ pressed) == 0x00)) {pressed&=~0x08;uart_putstr(UART_1, "Key4 弹起");}delay(200);
}void main(void) {setup();while(1){loop();}
}
运用下刚刚推导的结论,拿案例中的第三个按钮的判断语句来修改,如下所示:
if(K3 == 0 && (((1<<2) & ~pressed) == (1<<2))) {pressed|=(1<<2);uart_putstr(UART_1, "Key3 按下");
} else if(K3 == 1 && (((1<<2) &~ pressed) == 0x00)) {pressed&=~(1<<2);uart_putstr(UART_1, "Key3 弹起");
}
进一步改进,声明一个 8 位的变量 flag
来存储位移运算的结果。所以 loop
函数改造如下:
uint8 flag = 0x00;// 用来临时存储位移运算结果void loop() {// 0000 0001flag = 1<<0;if(K1 == 0 && ((flag & ~pressed) == flag)) {pressed|=flag;uart_putstr(UART_1, "Key1 按下");} else if(K1 == 1 && ((flag &~ pressed) == 0x00)) {pressed&=~flag;uart_putstr(UART_1, "Key1 弹起");}// 0000 0010flag = 1<<1;if(K2 == 0 && ((flag & ~pressed) == flag)) {pressed|=flag;uart_putstr(UART_1, "Key2 按下");} else if(K2 == 1 && ((flag &~ pressed) == 0x00)) {pressed&=~flag;uart_putstr(UART_1, "Key2 弹起");}// 0000 0100flag = 1<<2;if(K3 == 0 && ((flag & ~pressed) == flag)) {pressed|=flag;uart_putstr(UART_1, "Key3 按下");} else if(K3 == 1 && ((0x04 &~ pressed) == 0x00)) {pressed&=~flag;uart_putstr(UART_1, "Key3 弹起");}// 0000 1000flag = 1<<3;if(K4 == 0 && ((flag & ~pressed) == flag)) {pressed|=flag;uart_putstr(UART_1, "Key4 按下");} else if(K4 == 1 && ((flag &~ pressed) == 0x00)) {pressed&=~flag;uart_putstr(UART_1, "Key4 弹起");}delay(200);
}
其实,该案例如果能用循环将 四个 开关置入循环体来指代,会使得代码简化非常多。这个问题先搁置,等待后续学习。
最后,附加四个位运算的公式:
uint8 variable = 0x00;
uint8 n = 1;
// 将第 n 位置为 1
variable = variable | (1 << n);
// 将第 n 位置为 0
variable = variable & ~(1 << n);
// 将第 n 位置取反
variable = variable ^ (1 << n);
// 取出第 n 位的值
variable = (variable & (1 << n)) >> n;