[应用相关] 单片机学习中的常见问题

[复制链接]
3306|78
 楼主| 工程师犹饿死 发表于 2021-11-19 23:47 | 显示全部楼层
打印出i的值为 “1”(基于芯片STC12C5A60S1,11.0592MHZ,其他型号的单品机可能打印出来的效果不同,甚至同样的型号单片机、同样的晶振、同样型号的1602 操作打印出来的值也是不一样的),又变化为:
 楼主| 工程师犹饿死 发表于 2021-11-19 23:47 | 显示全部楼层
  1. unsigned char i=0 ;/*同时,需要注意这里,修改了类型*/
  2. do{
  3. i++;
  4. }while( ((lcd1602ReadStatus()) & 0x80)  && (i<10));  /*经过验证,10已经可以满足足够的时间了*/
 楼主| 工程师犹饿死 发表于 2021-11-19 23:48 | 显示全部楼层
这样做才是安全的。(尤其在大系统中,不会因为1602的”不存在“导致整个系统的运行失败,经典案例如下)

(在2015年2月7日,我帮助一个大四学生调试毕设时,也发现了同样的问题,此同学在1602液晶屏的写命令、写数据函数里面加入了判忙函数,判忙核心代码如下:

  1. do{
  2. ;
  3. }while( (lcd1602ReadStatus()) & 0x80));
 楼主| 工程师犹饿死 发表于 2021-11-19 23:49 | 显示全部楼层
,最后因在系统设计时,暂时用了1602显示,后来为了做上位机,就用了串口通信代替1602, 拔掉了1602液晶屏,但是没有屏蔽掉主函数中关于1602的操作,导致了程序卡死在判忙函数中,因为如果没有硬件连接,通过数据口读取到的1602状态数据是0xff,然后0xff&0x80,这样得到的结果永远是1(除非再次插上液晶屏或者手动将数据最高bit对应的IO口拉低),导致程序卡死在这里,而且这些功能都是封装好的,所以就更加加大了难度,导致一周没有调试出来,后来,我给他指出两种方法(1)在主函数中屏蔽所有关于1602液晶屏的函数,实验后,系统运行正常(2)修改判忙函数,因为一劳永逸,试验后,运行正常,即使主函数中没有屏蔽1602相关函数,1602硬件暂时取下,也是可以成功运行的)
 楼主| 工程师犹饿死 发表于 2021-11-19 23:50 | 显示全部楼层
另外,还有很多同**用了延时的方法去操作,似乎大部分人都得到了满意的结果,那么,这里有出现了两个问题:(1)延时的结果有三种:延时太小、延时正好、延时太大,对于大家来说,延时正好、延时太大都是可以完成系统的设计的,但是,你又如何保证你的延时不会小呢?大部分人想,这非常简单,把延时设计的非常大就好了,那么,就引出了问题(2)延时太大,不利于系统整体设计,导致了效率太低 。 何去何从,大家可以想一想。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:50 | 显示全部楼层
问题0008:调试程序之“穷途末路”
有时候,在调试程序时,明明都是严格按照datasheet的说明和时序图编写的代码,就是没有效果,然后借鉴别人的代码,代码相同,但还是没有效果 。 这时候,先要怀疑硬件,如果硬件没有问题,有可能还会有一个原因,那就是单片机的问题(或者是keil软件的问题,这个不得而知了)。我之前遇到过编写一个代码,没有效果,最后把全部代码屏蔽掉,只在main函数中,点亮了LED,但还是失败了,后来,复制代码,重新新建工程,粘贴代码,就好了。(这里不是喷STC,其实STC在国内做的较好,但是总会有一些奇葩的时候),这里分享一下,仅个人经历。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:51 | 显示全部楼层
问题0009:如何一步步测试编写代码

单片机程序测试调试似乎对于刚入门的同学有一定难度,但是入门之后我们快速调试程序呢?很多人写代码一气呵成,然后在main函数中调用10几个子函数,然后直接通过液晶屏或者其他的手段观察效果是正确,这样的方法对吗 ?以下是常用方法:
 楼主| 工程师犹饿死 发表于 2021-11-19 23:52 | 显示全部楼层
   1.借助于网上的视频讲解或源代码,(对于32位的MCU尚可理解,但是51、PIC.MSP430单片机使用者如果入门后还是同样的方法,你就OUT了),但只适用于部分网上资料较多的芯片。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:52 | 显示全部楼层
2.借助"逻辑分析仪"仪器去分析(这种神器我也是通过网上资料介绍,有所了解,但是并没有使用过,不过好像非常好用的),通过逻辑分析仪可以分析我们的时序是否存在问题,但是个人感觉比较适用于菜鸟和**单片机程序的高手,对于大多数用户,没有必须花钱购买
 楼主| 工程师犹饿死 发表于 2021-11-19 23:53 | 显示全部楼层
3.利用程序本身去验证程序,编写一个函数,测试一个函数,操作简单,屡试不爽,这里以大家熟知的51类单片机STC12C5A60S2和数字温度传感器DS18B20为例(单总线协议通信)。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:53 | 显示全部楼层
下图为DS18B20的“复位”时序,因为每次操作都需要MCU发送一个复位信号,所以,“复位”函数相当重要。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:54 | 显示全部楼层
 楼主| 工程师犹饿死 发表于 2021-11-19 23:55 | 显示全部楼层
我们知道,当DS18B20存在时,红色部分的时间内,总线将会被DS15B20拉低,呈现低电平   ; 如果DS15B20不存在,则在单片机使用总线(蓝色)后,红色部分仍然是高电平。
 楼主| 工程师犹饿死 发表于 2021-11-19 23:56 | 显示全部楼层
假设我们有以下定义:
  1. typedef unsigned char UB8 ;
  2. typedef unsigned short int UW16 ;
  3. typedef unsigned long UL32 ;

  4. typedef char SB8;
  5. typedef short int SW16 ;
  6. typedef long SL32 ;
  7.        
  8. #define HIGH_LEVEL 1       
  9. #define LOW_LEVEL  0

  10. sbit ds18b20_io_bit        = P1^7 ;/*根据硬件选择*/

  11. #define DS18B20_EXISTENCE                        0 /*存在DS18B20*/
  12. #define DS18B20_NOT_EXISTENCE                -1/*不存在DS18B20*/
 楼主| 工程师犹饿死 发表于 2021-11-19 23:56 | 显示全部楼层
然后编写“复位函数”

  1. /******************************************************
  2. Function        :ds18b20Init
  3. Input                :N/A
  4. Output                :N/A
  5. Return                :DS18B20_EXISTENCE或者DS18B20_NOT_EXISTENCE
  6. Description        :N/A
  7. Note                :经过测试在11.0592MHZ和12.00MHZ条件下都可以
  8.                         检测到ds18b20的存在。
  9. ******************************************************/
  10. SB8 ds18b20Init(void)
  11. {

  12.         SB8 existenceFlag ;
  13.         UB8 i=0 ;
  14.        
  15.         ds18b20_io_bit = HIGH_LEVEL ;
  16.         _nop_() ;
  17.         _nop_() ;       

  18.         /*master transmit the reset pulse */
  19.         ds18b20_io_bit = LOW_LEVEL ;
  20.         delay720usForDs18b20();

  21.         /*rising edge*/
  22.         ds18b20_io_bit = HIGH_LEVEL ;

  23.         /*ds18b20 wait 15us to 60us then transmit a presence pulse by
  24.         pulling the 1-wire low for 60us to 240 us */

  25.         delay15usForDs18b20();
  26.         delay15usForDs18b20();
  27.         delay15usForDs18b20();
  28.         delay15usForDs18b20();

  29.        
  30.         if( ! ds18b20_io_bit)
  31.         {/*ds18b20 eixstence*/
  32.                 existenceFlag = DS18B20_EXISTENCE;
  33.         }
  34.         else
  35.         {/*no ds18b20*/       
  36.                 existenceFlag = DS18B20_NOT_EXISTENCE;                          
  37.         }

  38.         while((!ds18b20_io_bit) && (i<250))
  39.         {
  40.                 i++;/*wait*//*暂时,具体见调试*/
  41.                 /*经过测试,这里的250对于11.0592MHZ和12MHZ都远远足够了*/
  42.         }

  43.         return existenceFlag ;
  44. }
 楼主| 工程师犹饿死 发表于 2021-11-19 23:57 | 显示全部楼层
然后,我们验证此函数是否可以成功完成“复位功能”:(时序不再详细分析)
  1. #include <reg52.h>
  2. #include "ds18b20.h"


  3. void main(void)
  4. {
  5.         SB8 flag = -3 ;

  6.         P1=0x00;//连接leds,高电平点亮
  7.        
  8.         flag = ds18b20Init();

  9.                 if( flag == DS18B20_EXISTENCE) //检测结果:存在,0
  10.                 {
  11.                         P1 = 0x01 ;
  12.                 }
  13.                 else if(flag == DS18B20_NOT_EXISTENCE)//检测结果:<span style="font-family: Arial, Helvetica, sans-serif;">不存在,-1</span>
  14.                 {
  15.                         P1=0x02;
  16.                 }
  17.                 else
  18.                 {
  19.                         P1 = 0x04 ;
  20.                 }
  21.                 while(1) ;
  22. }
 楼主| 工程师犹饿死 发表于 2021-11-19 23:58 | 显示全部楼层
这样,我们通过LED就可以验证函数了:
       (1)正常连接DS18B20,结果如果是P1的第一个灯亮了(P1=0x01),则表示单片机可以检测到DS18B20的存在,说明函数可能对了(可以检测到DS18B20的存在);
 楼主| 工程师犹饿死 发表于 2021-11-19 23:59 | 显示全部楼层
(2)拔掉DS18B20,结果如果是P1的第二个灯亮了(P1=0x02),则表示单片机未检测到DS18B20的存在,说明函数完全正确。(可以同时检测到DS18B20的存在和不存在)
aoyi 发表于 2021-12-11 09:16 | 显示全部楼层
这个还是dip封装的那种的吧
wiba 发表于 2021-12-11 09:17 | 显示全部楼层
我觉得第一条可以忽略了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部