xiaoxia9623
发表于 2025-2-19 13:51:25
xiaoxia9623 发表于 2025-2-19 13:48
第十四集讲的是矩阵按键,跟AI8051U中的矩阵按键 讲的是一样的。刚开始看到这个图的时候,我好一会没有明白 ...
这个R59和R60,这两个电阻在矩阵按键当中是没有用处的。不过它不影响什么。课里面没有讲到,好一会我一直在想,它们是做什么用处的,后来才想明白原来是没有用处啊!{:aixin:}
xiaoxia9623
发表于 2025-2-19 17:02:15
第十五集外部中断,本节课讲 了外部中断一共有五个,其中0各1具有边缘中断和下降沿中断,2 3 4 只有下降沿中断,还讲到了引脚是固定的,不能切换引脚 。值得注意的是,定时器零可设为不可被 打断的中断,这里讲优先级的时候,可以讲一下这个。只要这个中断正在执行,不会被其它任何中断再打断。不能被 中断嵌套。不过有一点不明白,为什么中断函数还需要在主函数里面?为什么不写在exrt.c里面呢?中断初始化函数和中断函数都应该在这里面啊。这明明是属于中断部分啊。{:tuosai:}
void INT0_Isr(void) interrupt 0
{
// SEG0 += 1;
}
void P3Exit_Isr(void) interrupt 40
{
u8 intf;
intf = P3INTF;
xiaoxia9623
发表于 2025-2-20 20:21:12
AUXR = 0x80; //Timer0 set as 1T, 16 bits timer auto-reload,
TH0 = (u8)(Timer0_Reload / 256);
TL0 = (u8)(Timer0_Reload % 256);
ET0 = 1;
TR0 = 1;
EA = 1;
for(i=0; i<8; i++)LED8 = 0x10;
今天看了官方的NTC测温的事例代码。在定是器0的自动重装载模式下,重载值的写法可以有三种:
1. TH0 = (u8)(Timer0_Reload / 256);
TL0 = (u8)(Timer0_Reload % 256);
(u8)(Timer0_Reload / 256):
Timer0_Reload / 256 进行的是整数除法运算。在二进制里,除以 256相当于把这个 16 位的数右移 8 位,得到的结果就是这个 16 位数值的高 8 位。
(u8) 是强制类型转换,把结果转换为 8 位无符号整数,然后赋值给 TH0 寄存器。
(u8)(Timer0_Reload % 256):
Timer0_Reload % 256 进行的是取模运算。取模 256 可以得到这个 16 位数值的低 8 位。
同样通过 (u8) 强制类型转换为 8 位无符号整数,再赋值给 TL0 寄存器。
2. TH0 = (u8)(Timer0_Reload>>8);
L0 = (u8)(Timer0_Reload );
(u8)(Timer0_Reload>>8):
Timer0_Reload>>8 是位运算,将 Timer0_Reload 这个 16 位的数右移 8 位,这样就把高 8 位移动到了低 8 位的位置。
然后通过 (u8) 强制类型转换为 8 位无符号整数,赋值给 TH0 寄存器。
(u8)(Timer0_Reload ):
直接把 Timer0_Reload 进行 (u8) 强制类型转换,由于 (u8) 是 8 位无符号整数类型,所以会截取 Timer0_Reload 的低 8 位,赋值给 TL0 寄存器。
两者有如下的不同之处。
运算效率:
位运算(第二组代码)的执行速度通常比除法和取模运算(第一组代码)要快。因为除法和取模运算在硬件实现上相对复杂,需要更多的时钟周期;而位运算直接对二进制位进行操作,硬件实现简单,速度快。
代码可读性:
第一组代码使用除法和取模运算,从数学角度更容易理解,对于不熟悉位运算的开发者来说,可能更直观。
第二组代码使用位运算,对于熟悉二进制和位操作的开发者来说,能更清晰地看出是在进行高低 8 位的拆分,代码更简洁。
3.TH0 = (u8)(Timer0_Reload>>8);
TL0 = (u8)(Timer0_Reload & 0xFF);
第二组代码中 TL0 = (u8)(Timer0_Reload ); 会导致 Timer0_Reload 的高 8 位丢失,但只要在程序上没有修改过Timer0_Reload的值 ,就不需要要写第三种。
xiaoxia9623
发表于 2025-2-20 20:42:37
xiaoxia9623 发表于 2025-2-20 20:21
AUXR = 0x80; //Timer0 set as 1T, 16 bits timer auto-reload,
TH0 = (u8)(Timer0_Reload / 256) ...
因为寄存器是个影子寄存器,重新装入的时候,会存入影子值,它因为失去了与Timer0_Reload的关系,所以如果修改了Timer0_Reload的值,TL0不会更新,所以会出错。
xiaoxia9623
发表于 2025-2-20 21:00:58
第十三集第二小节中讲写P6会乱码,其实应该不会,因为#define SEG_SEGP6所以编译器在编译的时候会把SEG_SEG全部替换会P6啊,因为是分时啊。又不在同一个时间,怎么会乱码呢?变量只是相当于两个不同的事物,然后起了两个不同的名称,根乱不乱码没有关系!不过这样看起来容易区分,思路清晰一些。
xiaoxia9623
发表于 2025-2-20 21:31:38
第十三集第三小节u16 Count = {0,0,0,0,0,0,0,0};这里定义了8个成员的数组,还可以写为u16 Count = {};和u16 Count[] = {0,0,0,0,0,0,0,0};因为声明了有8个成员,所以就不需要列出了,后面既然列出了8个成员,所以也就不需要声明8个了。冲哥很细心,即列出成员,又声明了成员个数,这样让我们这些学生能更好的理解。
for(i=0;i<8;i++)
{
if( ~KEY & ( 1<<i ) ) //可是这里与个1干嘛呀?与跟不与没有区别,不与1它也能进,而且它也只能是这个位。
{
if( Count<60000 )
xiaoxia9623
发表于 2025-2-20 22:30:56
xiaoxia9623 发表于 2025-2-20 21:31
第十三集第三小节u16 Count = {0,0,0,0,0,0,0,0};这里定义了8个成员的数组,还可以写为u16 Count = { ...
噢,搞错了。if( ~KEY ) 这一判断条件存在问题。此条件只是单纯检查 KEY(通常代表 P3 端口)是否有按键按下,并未具体针对当前循环中所对应的第 i 个按键进行判断。这就会造成无论当前循环到哪个按键,都会以整个 P3 端口是否有按键按下作为判断依据,从而无法准确区分每个按键的状态。 这个按键好难!看了5遍了,还是理解不了,特别是后面那个判断部分。
xiaoxia9623
发表于 2025-2-21 16:56:52
for(i=0;i<8;i++)
{
if( ~KEY & ( 1<<i ) ) //if ((KEY & (1 << i)) == 0) 这里这样写更直观一些
{
if( Count<60000 )
if (KEY == ~(1 << i))
[*]该条件判断 KEY 是否完全等于 ~(1 << i)。也就是说,KEY 的所有位都必须与 ~(1 << i) 的对应位相同,条件才会成立。例如,当 i = 2 时,只有当 KEY 为二进制 1111 1011 时,条件才为真。
[*]适用于需要精确匹配某个特定二进制模式的场景,即要求 KEY 的所有位都符合 ~(1 << i) 的值。
[*]只能按下一个按键并且是i位对应的这一个,才能进入IF语句。
if ((KEY & (1 << i)) == 0) 与 if( ~KEY & ( 1<<i ) )
[*]该条件判断 KEY 和 1 << i 按位与的结果是否为 0。这意味着只要 KEY 在第 i 位上是 0,条件就会成立,而 KEY 的其他位可以是任意值。例如,当 i = 2 时,只要 KEY 的第 2 位是 0,无论其他位是什么,条件都为真。
[*]常用于检查 KEY 的某一位是否为 0,而不关心其他位的值,例如在读取硬件寄存器的某一位状态时经常使用。
[*]可以同时按上多个按键,但是只有i位对应的这一个会被加一。
xiaoxia9623
发表于 2025-2-21 21:13:12
xiaoxia9623 发表于 2025-2-21 16:56
for(i=0;i
if ((KEY & (1 << i)) == 0) 与 if( ~KEY & ( 1<<i ) ) 其实在这节课里这两个表达方法起到的作用都是一样的,因为是轮询的方式加一,所以每次i等于多少,每次就只能在这一个位的值上加一。一个FOR循环下来,每个按下的位都加了一,也可以看作是同时加了一。
xiaoxia9623
发表于 2025-2-21 21:43:06
xiaoxia9623 发表于 2025-2-20 21:31
第十三集第三小节u16 Count = {0,0,0,0,0,0,0,0};这里定义了8个成员的数组,还可以写为u16 Count = { ...
u16 Count;定义的是全局变量,这样写就可以了。它们8个成员的初值都会默认为0