« 上一篇下一篇 »

问题总结

 1、如果使用一个宏定义包含多条语句或多个表达式,一定要加上花括号。

#define LED1_ON()    LED1 = 1; \
                     led1 = 1;
for (i = 0; i < n; i++)
    LED1_ON();

如上所示,如果for()这样接这个宏,则只有宏中定义的第一语句会被执行。原因是这里的宏没有加花括号,第2条语句不包含在循环执行动作里面,所以就会导致不易发现的bug。上述宏定义应该改成如下形式:

#define LED1_ON()    {LED1 = 1; \
                      led1 = 1;}

这样LED1_ON()这个宏定义的两条语句都会在循环中执行。当然,实际上这里的for()的写法也不够严谨,for()循环后面也应该加花括号,更好的方式应该改成如下形式:

#define LED1_ON()    {LED1 = 1; \
                      led1 = 1;}
for (i = 0; i < n; i++)
{
    LED1_ON();
}

这样的话,就有双重保障,保证宏的两条语句都能被同时执行了。

2、变量中某个位是否为高的判断表达式该怎么写

如下所述是一个错误的表达式,而且该错误也不容易被察觉:

uint8_t led_status = 0;
led_status |= (0x1<<1);
if (led_status & 0x02 == 1)
{
    // 执行相应操作
}

上述代码其实就是把led_status的bit1置高,然后在if()中判断该变量的bit是否为高,为高则执行相应的操作,但是这里最大的问题就是习惯性的用了平时的普通标志判断方式,判断按位与后的结果是否==1,也就是判断是否为真,然而这里绝对不能这么写,因为0x02&0x02=0x02,2当然不等于1,所以即使bit1被置高了,这里的判断始终不能为真,一直不能进入if()里面执行相应操作。

合理的写法是判断按位与是否为真,后面不要加==,不要去判断是否等于某个值,只要判断不为0就行了,正确形式如下:

uint8_t led_status = 0;
led_status |= (0x1<<1);
if (led_status & 0x02)
{
    // 执行相应操作
}

或者直接与0来做对比就好了,不为0就代表该位被置高了,如下:

uint8_t led_status = 0;
led_status |= (0x1<<1);
if (led_status & 0x02 != 0)
{
    // 执行相应操作
}

3、HC164与HC595有何不同

主要区别就一点,595可以对输出进行锁存,即可以把8个输出脚的值全部更新之后,再重新刷新输出,而在更新值的过程中,实际输出的是上一次的值。而164则不行,也就是说,164在更新输出值的时候,实际的输出是同时在变的,它没法等到全部8个脚的值都更新之后,再来控制统一输出,而是在更新过程中同时实际输出值也发生了改变。

4、软件模拟UART通信延时的问题

重点是定时器的控制,我试过一直开着定时器,然后每次发送根据定时器的时间来进行延时,这样的话,连续发送是不会出错的,但是如果发送一段数据后,延时一段时间再发送另一段数据,则会有个别字节是错误的。原因是什么呢,其实就是定时器的定时不精确,因为定时器一直开着,比如我设定是9600波特率,则104us定时器会中断一次更新计时变量的值,但是在一直开着的情况下定时器是一直在跑的,而间隔了一段时间发送时,此时第一个位的延时很大概率上不足104us,比如说现在定时器的值是50,我发了起始位数据后,要延时到51再发下一位数据,但是此时时钟在新一轮中断到来前已经跑了50us,那么到下一次中断,本次的延时时间只有104-50=54us,那么这个时间自然就不对了,从而导致时序不符合UART的协议了,所以数据就错了。然后我试过在每次发送前重载定时器的值,也是会出错,按理来说,如果重载后重新开始计数,应该是不会出错的,这里出错了,可能是因为定时没有重新刷新这个计数的值。那么该如何处理这个问题呢?发送前后开关定时器就好了,即我在发送前打开定时器,发送完之后马上关闭定时器,这样一来,定时器只有在数据发送过程中才会运行了,时间也就不会出现不准的情况了。用这种方式,我测试了10K字节的发送,数据全部是正确的,这种方案是完全可行的。