灞波儿奔

  • 2019-03-25
  • 发表了主题帖: TVS管选型

         选用TVS管步骤如下:确定待保护电路的直流电压或持续工作电压。——>  TVS管的反向变位电压即工作电压(VRWM)--选择TVS管的VRWM等于或大于上述步骤 1 所规定的操作电压。这就保证了在正常工作条件下TVS管吸收的电流可忽略不计,如果步骤 1 所规定的电压高于TVS管的 VRWM ,TVS管将吸收大量的漏电流而处于雪崩击穿状态,从而影响 电路的工作。——> 最大峰值脉冲功率:确定电路的干扰脉冲情况,根据干扰脉冲的波形、脉冲持续时间, 确定能够有效抑制该干扰的TVS管峰值脉冲功率。——>  所选TVS管的最大箝位电压(VC)应低于被保护电路所允许的最大承受电压。——>  单极性还是双极性—常常会出现这样的误解即双向TVS管用来抑制反向浪涌脉冲,其实并 非如此。 双向TVS管用于交流电或来自正负双向脉冲的场合。TVS管有时也用于减少电容。 如果电路只有正向电平信号,那麽单向TVS管就足够了。          TVS管操作方式如下:正向浪涌时,TVS管处于反向雪崩击穿状态;反向浪涌时,TVS管类似正向偏置二极管一样导通并吸收浪 涌能量。在低电容电路里情况就不是这样了。应选用双向TVS管以保护电路中的低电容器 件免受反向浪涌的损害。——> 如果知道比较准确的浪涌电流 IPP,那么可以利用 VC 来确定其功率,如果无法确定功率 的大概范围,一般来说,选择功率大一些比较好。   ① 确定被保护电路的最大直流工作电压或连续工作电压、电路的额定标准电压和“高端”容限;   ② TVS管额定反向关断电压VFM应大于或等于被保护电路的最大工作电压。若选用的VFM太低,器件可能进入雪崩或因反向漏电流太大影响电路的正常工作。TVS串联连接可以提高TVS的总额定反向关断电压;   ③ TVS管的最大箝位电压VC应小于被保护电路的损坏电压;   ④ 在规定的脉冲持续时间内,TVS管的最大峰值脉冲功耗PM必须大于被保护电路内可能出现的峰值脉冲功率。在确定了最大箝位电压后,其峰值脉冲电流应大于瞬态浪涌电流;   ⑤ 对于数据接口电路的保护,还必须注意选取具有合适结电容Cj的TVS器件;   ⑥ 根据用途选用TVS管的极性及封装结构。交流电路选用双极性TVS管较为合理;多线保护选用TVS管阵列更为有利;   ⑦ 温度考虑。瞬态电压抑制器TVS管可以在-55℃~+150℃之间工作。如果需要TVS管在一个变化的温度环境下工作,由于其反向漏电流ID是随工作温度的增加而增大,功耗随TVS管结温的增加而下降,因此,使用时须考虑温度变化对其工作特性的影响。

  • 发表了主题帖: 物联网设备的显示技术

        考虑物联网设备等作为上述环境传感器。对于这些设备,最小化其功率使用以延长充电周期之间的操作时间是至关重要的。这些设备没有能量预算来运行背光,高分辨率彩色显示器。另一方面,在许多情况下,在每个设备上显示一个显示简单信息(如电池电量或系统诊断数据),而不必通过使用其他设备登录云来获取数据基于服务器的双稳态显示器,例如电子纸,在亚马逊Kindle等电子阅读器中取得了巨大成功,适合这种应用,有两个主要原因为了这。首先,显示器只在变化时消耗能量(图1)。这节省了其他显示技术用于刷新静态显示的能量。第二个是电子纸显示器是反射性的,因此不需要耗能的背光源,因为它们的内容是可见的 - 至少在合理的环境光下。   图1:电子纸是黑色或白色,具体取决于电极的电荷。  这种低能耗技术为即使是最简单的物联网设备添加显示器创造了机会,因此它们可以呈现直接的,人类可读的界面。如果您的物联网设备可以从其云端接收数据并发送数据,则可以创建机会,例如,显示您当前信用余额的支付卡,或者包含可扫描条目代码或特殊优惠通知的会员卡。   当然,在物联网设备需要显示快速变化的数据或在黑暗的地方工作的情况下,电子纸显示效果要差得多。对于这种应用,TFT LCD和OLED可以提供有用的替代品。   LCD在过去几十年中的质量得到了极大的改善,这得益于它们在平板电视,手机和电脑中的广泛应用。显示器。一些屏幕的分辨率现在非常高,以至于几乎不可能用肉眼看到单个像素,而游戏玩家和动作电影爱好者已经将显示器刷新率提升到可以清晰显示最快动作的程度。   这意味着LCD最适合用于需要丰富多媒体体验的物联网应用,或者设计用于光线昏暗的地方,背光可以提供必要的屏幕对比度以确保易读性。出于同样的原因,LCD在强光下的可读性较差,因为这会降低对比度。   然而,液晶显示器本身就是比电子纸更耗能的显示技术,因此最好保留用于物联网设备,例如永久供电的恒温器,或用于移动电话的设备,其用户乐于定期为其充电。 LCD需要背光的事实也意味着LCD模块将比电子纸等效(图2)更厚,而高信息内容显示器的复杂驱动电子设备也会带来成本和能耗缺点。       图2:显示典型TFT LCD结构的横截面图。  物联网设备显示器的第三个选项是OLED技术,已开始出现在高端电视,手机和智能手表中。 OLED在理论上具有比TFT LCD更好的功率效率,因为它们使用电致发光材料在施加电流时发光,而不是像液晶显示器那样使用液晶材料作为背光上的快门(图3)。        图3:OLED显示屏的典型横截面图。  OLED可以控制单个像素的亮度,使显示纯黑色和非常高的对比度。它们还提供比许多LCD更好的视角,并且可以在塑料基板上制作,以创建独特的形状,如Apple Watch。然而,与大多数技术一样,OLED具有缺点,例如一些电致发光材料的老化问题,对水的敏感性,这往往需要昂贵的封装,以及与LCD相同的日光可读性问题。此外,与液晶显示器相比,它们仍然相对昂贵。   物联网一词如此广泛,几乎毫无意义。为您的物联网设计选择正确的显示只是意味着了解应用程序的要求,然后选择匹配的技术。聪明的设计师还将确保他们了解电子纸等可用技术如何为其物品带来额外的,或许意外的功能,从而帮助他们将其与构成物联网的数十亿其他设备区分开来。

  • 2019-03-22
  • 发表了主题帖: SMBus与I2C的差别

    SMBus与I2C的差别      SMBus与I2C总线之间在时序特性上存在一些差别。首先,SMBus需要一定数据保持时间,而 I2C总线则 是从内部延长数据保持时间。SMBus具有超时功能,因此当SCL太低而超过35 ms时,从器件将复位正在进行的通信。相反,I2C采用硬件复位。SMBus具有一种警报响应地址(ARA),因此当从器件产生一个中断时,它不会马上清 除中断,而是一直保持到其收到一个由主器件发送的含有其地址的ARA为止。SMBus只工作在从10kHz到最高100kHz。最低工作频率10kHz是由SMBus超时功能决定的。

  • 发表了主题帖: SPI、UART和IIC区别

    SPI:高速同步串行口。3~4线接口,收发独立、可同步进行 UART:通用异步串行口。按照标准波特率完成双向通讯,速度慢 I2C:一种串行传输方式,三线制 3根线实现数据双向传输 串行外围接口 Serial peripheral interface UART:通用同步异步收发器 UART是用于控制计算机与串行设备的芯片。有一点要注意的是,它提供了RS-232C数据终端设备接口,这样计算机就可以和调制解调器或其它使用RS-232C接口的串行设备通信了。作为接口的一部分,UART还提供以下功能: 将由计算机内部传送过来的并行数据转换为输出的串行数据流。将计算机外部来的串行数据转换为字节,供计算机内部使用并行数据的器件使用。在输出的串行数据流中加入奇偶校验位,并对从外部接收的数据流进行奇偶校验。在输出数据流中加入启停标记,并从接收数据流中删除启停标记。处理由键盘或鼠标发出的中断信号 (键盘和鼠票也是串行设备)。可以处理计算机与外部串行设备的同步管理问题。有一些比较高档的UART还提供输入输出数据的缓冲区,现在比较新的UART 是16550,它可以在计算机需要处理数据前在其缓冲区内存储16字节数据,而通常的UART是8250。现在如果您购买一个内置的调制解调器,此调制解 调器内部通常就会有16550 UART。 I2C:能用于替代标准的并行总线,能连接的各种集成电路和功能模块。I2C是多主控总线,所以任何一个设备都能像主控器一样工作,并控制总线。 总线上每一个设备都有一个独一无二的地址,根据设备它们自己的能力,它们可以作为发射器或接收器工作。多路微控制器能在同一个I2C总线上共存。 更详细的区别: 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS:意为IC之间总线) UART(Universal Asynchronous Receiver Transmitter:通用异步收发器) 第二,区别在电气信号线上: SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。SPI总线可以实现多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信,当有多个从设备时,还可以增加一条从设备选择线。 如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO),一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。 I2C总线是双向、两线(SCL、SDA)、串行、多主控(multi-master)接口标准,具有总线仲裁机制,非常适合在器件之间进行近距离、非经常性的数据通信。在它的协议体系中,传输数据时都会带上目的设备的设备地址,因此可以实现设备组网。 如果用通用IO口模拟I2C总线,并实现双向传输,则需一个输入输出口(SDA),另外还需一个输出口(SCL)。 UART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。 显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。 第三,从第二点明显可以看出,SPI和UART可以实现全双工,但I2C不行; 第四,看看牛人们的意见吧! wudanyu:I2C线更少,我觉得比UART、SPI更为强大,但是技术上也更加麻烦些,因为I2C需要有双向IO的支持,而且使用上拉电阻,我觉得 抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信。SPI实现要简单一些,UART需要固定的波特率,就是说两位数据的间隔要相 等,而SPI则无所谓,因为它是有时钟的协议。 quickmouse:I2C的速度比SPI慢一点,协议比SPI复杂一点,但是连线也比标准的SPI要少。 SPI、I2C、UART三种串行总线协议的区别 SPI(Serial Peripheral Interface:串行外设接口) I2C(INTER IC BUS) UART(Universal Asynchronous Receiver Transmitter:通用异步收发器) SPI The SPI includes these distinctive features: Master mode and slave mode Bi-directional mode Slave select output Mode fault error flag with CPU interrupt capability Double-buffered data register Serial clock with programmable polarity and phase Control of SPI operation during wait mode SPI 有两种模式,Normal Mode and Bidirectional Mode,包括以下几根线: SS Slave Select SCK Serial Clock MOSI Master Output, Slave Input MISO Master Input, Slave Output MOMI Master Output, Master Input SISO Slave Input, Slave Output 其中前四根线用于Normal Mode ,常用的也是4根线的Normal Mode 。 MOSI This pin is used to transmit data out of the SPI module when it is configured as a Master and receive data when it is configured as Slave. MISO This pin is used to transmit data out of the SPI module when it is configured as a Slave and receive data when it is configured as Master. SS This pin is used to output the select signal from the SPI module to another peripheral with which a data transfer is to take place when its configured as a Masterand its used as an input to receive the slave select signal when the SPI is configured as Slave. SCK This pin is used to output the clock with respect to which the SPI transfers data or receive clock in case of Slave. SPI 是一种允许一个主设备启动一个与从设备的同步通讯的协议,从而完成数据的交换。也就是SPI是一种规定好的通讯方式。这种通信方式的优点是占用端口较少,一般4根就够基本通讯了。同时传输速度也很高。一般来说要求主设备要有SPI控制器(但可用模拟方式),就可以与基于SPI的芯片通讯了。 SPI 的通信原理很简单,它需要至少4根线,事实上3根也可以。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时 钟),CS(片选)。其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许 在同一总线上连接多个SPI设备成为可能。 接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原 因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上沿或下沿时改变,在紧接着的下沿或上沿被读取。 完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。 要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。 这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。 SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。 不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。 I2C 只要求两条总线线路:一条串行数据线SDA 一条串行时钟线SCL 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机从机关系软件设定地址主机可以作为主机发送器或主机接收器 它是一个真正的多主机总线如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁,防止数据被破坏 串行的8 位双向数据传输位速率在标准模式下可达100kbit/s 快速模式下可达400kbit/s 高速模式下可达3.4Mbit/s 片上的滤波器可以滤去总线数据线上的毛刺波保证数据完整 连接到相同总线的IC 数量只受到总线的最大电容400pF 限制 UART UART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。 显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。 UART常用于控制计算机与串行设备的芯片。有一点要注意的是,它提供了RS-232C数据终端设备接口,这样计算机就可以和调制解调器或其它使用RS-232C接口的串行设备通信了。 明显可以看出,SPI和UART可以实现全双工,但I2C不行

  • 2019-03-19
  • 发表了主题帖: MSP430简单程序设计

    1.实验目的     学会用C语言进行简单程序设计。 2.实验要求     掌握编写排序程序。 3.实验内容 (1)设计一个对8个数据(0-255,任意设置)的由小到大排序程序。 (2)将结果显示在LCD显示器上。 #include <msp430x14x.h> #include "Config.h" #include"1602.c" void main(void) {      WDT_Init();                         //看门狗设置      Clock_Init();                       //系统时钟设置      Port_init();                        //系统初始化,设置IO口属性      delay_ms(100);                      //延时100ms      LCD_init();                         //液晶参数初始化设置      LCD_clear();                        //清屏            unsigned int a[10] = {6,4,1,0,5,7,3,2};      int i = 0;      int j = 0;     for(i = 0;i < 8; i++)   {     for(j = i+1; j < 8;j++)     {       if(a > a[j])       {         int t = a;         a = a[j];         a[j] = t;       }     }   }                              while (1)       {                 i = 1;              //字符串输出显示                 LCD_clear();                 LCD_write_str(0,0,"Array");                 for(i=0; i < 15; i++){                   if(i % 2 == 0)                     LCD_write_char(i,1, a[i/2]+'0');                   else                     LCD_write_char(i,1, '<');                 }                                    delay_ms(2000);                       } }

  • 发表了主题帖: CCS中的DSP代码优化指令

    restrict的作用就是限制一个指针对一块内存的访问,进一步说就是如果一块内存区域通过一个受限制指针访问,那么它就不能通过另一个受限指针访问。 引入restrict的目的是确保同一块内存上没有其它引用,让编译器更好地优化指令,生成更有效的汇编代码。 把#pragma MUST_ITERATE( , , )放在循环体之前,告知开发板循环次数,改善软件流水 MUST_ITERATE告诉编译器循环的属性,但是这些属性必须是真实的,不然程序可能运行出错。此指令主要用于优化C函数循环,一般情况下,只要有循环都最好带上此指令 #pragma MUST_ITERATE(min, max, multiple);其中multiple参数必须有,循环执行次数必是multiple的整数倍。 这个信息对编译器使用软件流水技术非常重要 那min和max是最大和最小循环次数 const short *restrict x; //Assumption: Vectors x and h are double-word aligned _nassert((int)x % 8 == 0); 是个断言语句。 对条件进行判断,如果为1,通过,为0,则抛出异常。 通过对printf() 和 LOG_printf() 运行时间作比较发现,在C6211运行在150MHz的情况下,printf()需花费4000个周期约26.7μs, LOG_printf()只花费36个周期约0.24μs。printf() 比 LOG_printf() 多开销100倍以上的时间,因此 LOG_printf() 对于实时地显示一些运行状态是非常有帮助的。而且对于熟悉C语言的开发者来说, LOG_printf() 的调用格式几乎与printf() 完全一样。 就象大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。 volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.   简单地说就是防止编译器对代码进行优化.比如如下程序:   XBYTE[2]=0x55;   XBYTE[2]=0x56;   XBYTE[2]=0x57;   XBYTE[2]=0x58;   如果对外部硬件上述四条语句分别表示不同的操作,会产生四种不同的动作,那么编译器就不能像对待纯粹的程序那样对上述四条语句进行优化只认为XBYTE[2]=0x58;而忽略前三条语句(即只产生一条机器代码),此时编译器会逐一的进行编译并产生相应的机器代码(四条).  一般说来,volatile用在如下的几个地方:   1、中断服务程序中修改的供其它程序检测的变量需要加volatile;   2、多任务环境下各任务间共享的标志应该加volatile;   3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;   另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实   现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

  • 发表了主题帖: CCS编译优化与volatile

    在保存几百K的数据参数时,因为擦写Flash的时间较长,擦写过程中没能喂看门狗而导致系统重启,写数据失败。于是我考虑改写Flash的擦写函数,在擦写过程中加入喂狗函数。我在bsl源码中找到了EVMDM642_FLASH_write()和EVMDM642_FLASH_erase(),拷贝到我的工程,修改了函数名,编译后用仿真器加载Debug版测试通过,而后将Release版烧写到DSP上测试,却发现根本没有擦写过一个字节。 将整个工程的Opt Level设为None还是比较可惜的,因为Opt Level = File确实对程序的执行速度优化了。解决方法有两个: 1. 对函数中所有变量加volatile(推荐) 例如将 *((Uint8 *)EVMDM642_FLASH_BASE+0xAAA) = 0xaa; 改为 *((volatile Uint8 *)(EVMDM642_FLASH_BASE+0xAAA)) = 0xaa; 2. 将函数放入单独的文件中,将该文件的“File Specific Options –> Compiler”的Opt Level设为None

  • 发表了主题帖: 中断服务程序编写规则

    关于编写中断服务程序的一些基本原则: 1. 避免在中断服务程序中做浮点运算          好的中断服务程序应该遵循短而有效这一原则,但在中断服务程序中做浮点运算却大大地违背这一原则,同时有些处理器/编译器就不允许在中断服务城中做浮点运算。 2.中断服务程序不能有返回值         所有的中断服务程序都应该将返回类型定义为void。 3.中断服务程序不能传递参数        所有的中断服务程序的参数列表为void。 中断服务程序样例:   interrupt void timer_isr(void) { }

  • 发表了主题帖: C语言函数之中断函数

    在开始写中断函数之前,我们来一起回顾一下,单片机的中断系统。       中断的意思(学习过微机原理与接口技术的同学,没学过单片机,也应该知道),我们在这里就不讲了,首先来回忆下中断系统涉及到哪些问题。      (1)中断源:中断请求信号的来源。(8051有3个内部中断源T0,T1,串行口,2个外部中断源INT0,INT1(这两个低电平有效,上面的那个横杠不知道怎么加上去))      (2)中断响应与返回:CPU采集到中断请求信号,怎样转向特定的中断服务子程序,并在执行完之后返回被中断程序继续执行。期间涉及到CPU响应中断的条件,现场保护,现场恢复。      (3)优先级控制:中断优先级的控制就形成了中断嵌套(8051允许有两级的中断嵌套,优先权顺序为INT0,T0,INT1,T1,串行口),同一个优先级的中断,还存在优先权的高低。优先级是可以编程的,而优先权是固定的。       80C51的原则是①同优先级,先响应高优先权②低优先级能被高优先级中断③正在进行的中断不能被同一级的中断请求或低优先级的中断请求中断。       80C51的中断系统涉及到的中断控制有中断请求,中断允许,中断优先级控制      (1)3个内部中断源T0,T1,串行口,2个外部中断源INT0,INT1      (2)中断控制寄存器:定时和外中断控制寄存器TCON(包括T0、T1,INT0、INT1),串行控制寄存器SCON,中断允许寄存器IE,中断优先级寄存器IP       具体的是什么,包括哪些标志位,在这里不讲了,所有书上面都会讲。       在这里我们讲下注意的事项      (1)CPU响应中断后,TF0(T0中断标志位)和TF1由硬件自动清0。      (2)CPU响应中断后,在边沿触发方式下,IE0(外部中断INT0请求标志位)和IE1由硬件自动清零;在电平触发方式下,不能自动清楚IE0和IE1。所以在中断返回前必须撤出INT0和INT1引脚的低电平,否则就会出现一次中断被CPU多次响应。      (3)串口中断中,CPU响应中断后,TI(串行口发送中断请求标志位)和RI(接收中断请求标志位)必须由软件清零。      (4)单片机复位后,TCON,SCON给位清零。 C51语言允许用户自己写中断服务子程序(中断函数) 首先来了解程序的格式: void 函数名() interrupt m [using n] {} 关键字 interrupt m [using n] 表示这是一个中断函数 m为中断源的编号,有五个中断源,取值为0,1,2,3,4,中断编号会告诉编译器中断程序的入口地址,执行该程序时,这个地址会传个程序计数器PC,于是CPU开始从这里一条一条的执行程序指令。 n为单片机工作寄存器组(又称通用寄存器组)编号,共四组,取值为0,1,2,3 中断号 中断源 0        外部中断0 1        定时器0 2        外部中断1 3        定时器1中断 4        串行口中断 这5个中断源的中断入口地址为:(在上一篇文章中讲到的ROM前43个存储单元就是他们,这40个地址用来存放中断处理程序的地址单元,每一个类中断的存储单元只有8B,显然不是中断处理的程序,而是存放着中断处理程序的真正地址) INT0:0003H    0 T0:   000BH    1 INT1:0013H    2 T1:   001BH    3 串口: 0023H    4 中断向量(中断入口地址)= 中断号x8 +3       前面m意思很清楚,不同的m值表示这个函数是针对不同的中断源,比如m为1是表示它是定时器0的中断函数, 如void time0() interrupt 1{}       那么后面的using n 又是什么意思呢?在正在执行一个特定任务时,有更紧急的事情需要CPU来处理,涉及到中断优先权。高优先权中断低优先权正在处理的程序,所以最好给每个优先程序分配不同的寄存器组。       CPU正在处理某个事件,突然另外一个事件需要处理,于是进入中断后,而你不想将现在执行的程序的各寄存器状态入栈,那么可以把这个中断程序放入另一个寄存器组,如切换到1组,然后退出中断时,再切回到0组(原来的程序在0组)。

  • 发表了主题帖: 积分电路电容上并电阻的作用

    在书(The Art of Electronics)上看到“If the residual drift of the integrator is still too large for a given application, it may be necessary to put a large resistor R2 across C to provide dc feedback for stable biasing.” 如果积分器的残余漂移仍然过大,对于一个给定的应用程序,可能有必要把一个大的电阻器R2电容 C的两端,以提供稳定的偏置直流反馈。 意思是可以增大R2和C的值,达到稳定的偏置电流。 文中主要是说明偏置的,加上R2后,运放有直流反馈,对稳定有益。 可以这样想,运放的放大倍数是非常大的,它对直流至截止频率处的信号都有同样的放大量, 如果没有R2时,可以理解为直流放大量为无穷大,那么,运放稍有点失调什么的,就会使输出电平在电源电压或者0电平

  • 发表了主题帖: SPI设备驱动学习BUG问题记录

    以及oled的驱动的编写,在进行驱动的调试过程中, spi_register_board_info注册硬件信息遇到了无法创建设备。 spi_register_board_info spi_match_master_to_boardinfo spi_new_device spi_add_device /* Chipselects are numbered 0..max; validate. */     if (spi->chip_select >= spi->master->num_chipselect) {         dev_err(dev, "cs%d >= max %d\n",             spi->chip_select,             spi->master->num_chipselect);         return -EINVAL;     }//打印错误信息,css>=max 1,意为用户的使用片选数量大于master的数量支持 阅读内核源码进行错误信息调试 错误调试过程: 首先通过打印log信息定位错误,但开始由于对整个文件不是很熟悉,因此问题的定位一直停留在对自己编写的设备文件和驱动文件,以及spi_master文件spi_s3c64xx.c文件中打印log。 1.发现spi__info.c文件的入口函数spi_register_board_info文件被调用,但是在master文件bus_num进行匹配成功文件创建过程spi_add_device中一直出现误信息,css>=max 1, 2.此时考虑是设备spi_master驱动文件spi_s3c64xx.c文件对应的设备出现错误,但是由于开始对内核文件的不熟悉,所以导致一直认为spi_master的设备文件在mach-itop4412.c中,所以错误一直被延续,无进展。 3.通过对韦老师的spi视频的第3课第4节SPI_OLED驱动编译安装_P的观看,大致确定与pi_master驱动文件spi_s3c64xx.c文件对应的设备文件在arm/mach-exynos/dev-spi.c,通过对这个文件的仔细分析,发现在6410以后的在进行spi的master设备信息描述时,未使用bus_num和num_cs。 static struct s3c64xx_spi_info exynos_spi2_pdata = {     .cfg_gpio = exynos_spi_cfg_gpio,     .fifo_lvl_mask = 0x7f,     .rx_lvl_offset = 15,     .high_speed = 1,     .clk_from_cmu = true,     .tx_st_done = 25,    }; struct platform_device exynos_device_spi2 = {     .name         = "s3c64xx-spi",     .id       = 2,     .num_resources    = ARRAY_SIZE(exynos_spi2_resource),     .resource     = exynos_spi2_resource,     .dev = {         .dma_mask       = &spi_dmamask,         .coherent_dma_mask  = DMA_BIT_MASK(32),         .platform_data = &exynos_spi2_pdata,     }, }; 4.但是为什么在master匹配成功调用spi_s3c64xx.c的probe函数还可以实现master->num_chipselect = sci->num_cs;于是我进行了打印log查看,当我修改exynos_spi2_pdata结构体的成员,为它添加bus_num,num_cs,但是发现num_cs的值是1,exynos_spi2_pdata其他成员修改后都可以改变,但是无论我如何修改都是1. 5.于是我在arm/mach-exynos/dev-spi.c目录仔细观察,发现这里的设备信息是如何注册到内核中的了,唯一的可能就是mach-itop4412.c中了,但是mach-itop4412.c有很多条件编译很复杂,找了很久都未发现其注册spi结构体的信息,于是我全局搜索exynos_device_spi2 发现在mach-itop4412.c被放入一个platform_device数组smdk4x12_devices中,调用集中注册。但是这样的思路就很清晰了。 6.但是我打印probe中的num_cs为什么不变了,这个问题让我很不能理解。在反复考虑平台设备注册后哪里可能被修改。无果后, 7.我在arch/arm/plat-samsung/include/plat/s3c64xx-spi.h文件中将struct s3c64xx_spi_info结构体成员num_cs删除,编译发现 probe函数中报错,当一个关键错误提醒了我arm/mach-exynos/dev-spi.c305行出错 error: ‘struct s3c64xx_spi_info’ has no member named ‘num_cs’ 发现arm/mach-exynos/dev-spi.c中有一个函数exynos_spi_set_info居然被调用过,于是全局搜索发现在mach-itop4412.c中调用过将exynos_spi_set_info(2, EXYNOS_SPI_SRCCLK_SCLK,ARRAY_SIZE(spi2_csi));将num-_cs设备为1,因而无论如何num_cs的值都无法被修改,都为1. 至此问题解决,但是在注册spi设备时提示cs0已经被使用,经过搜索发现一个spi设备SPI_RC522使用了该设备,于是我在make menuconfig中查找CONFIG_SPI_RC522,将其不选中,编译内核下载结果成功创建,装载设备驱动也成功。

  • 2019-03-17
  • 发表了主题帖: C2000GPRS连接失败问题

    备注:采用的是SIMCOM的SIM900模块 1. GPRS连接失败问题 测试9台C2000两次,两次之间是机器拿开电池,间隔40分钟左右,每次都出现一台(但不是同一台): ⑴9台中发现一台,连接失败,然后重新连接,测试3次都失败 ⑵下电(下电时间较短,约为1s,正常为5s),重新上电,检测不到SIM卡,再重新上下电,依旧检测不到SIM卡---挂起唤醒,重新上电,可以连接成功。 2. 问题分析与解决 查看了GPRS应用程序的流程: ⑴GPRS程序打开的时候,先是对GPRS模块上电. ⑵发送AT+CGREG?查询,根据返回值来判断是否注册成功, ⑶注册成功后,调用RasDial函数在RAS客户端和RAS服务器建立RAS连接,返回值为0表示拨号成功。如果连续2次拨号失败,就调用GPRS复位函数对GPRS模块复位。 后来向SIMCOM技术支持反馈此问题,他们建议不要对GPRS模块进行复位,根据他们的建议,我们不再调用GPRS复位函数,然后就不会出现此问题。 但还有个现象解释不通,就是出现GPRS连接失败的情况下,我挂起然后再唤醒,就正常了,而挂起只是把控制GPRS上电的GPD13和控制GPRS复位的GPD14引脚由之前的输出改为输入而已,唤醒之后恢复为原来的输出状态。

  • 发表了主题帖: Foobar2000分割APE注意事项

    了解APE的特性     APE是目前流行的数字音乐文件格式之一。与MP3这类有损压缩方式不同的是:APE是一种无损压缩音频技术,也就是说当你将从音频CD上读取的音频数据文件压缩成APE格式后,你还可以再将APE格式的音源还原,而还原后的音源与压缩前的一模一样,没有任何损失。同时也表明其压缩参数无论用哪个都无法改变音质。     另外,把音源压制成APE共有五级参数          Compress (fast): '-c1000'          Compress (normal): '-c2000'          Compress (high): '-c3000'          Compress (extra high): '-c4000'          Compress (insane): '-c5000'

  • 发表了主题帖: TMS320F2803x系列实时控制 MCU 技术文档

    C2000系列实时控制器简介: C2000 生产选择指南 sprufk8.pdf 数据表: 中文板:TMS320F28030/28031/28032/28033/28034/28035 Piccolo 微控制器 (Rev. I)  (zhcs864i.pdf) 英文版:TMS320F28030/28031/28032/28033/28034/28035 Piccolo Microcontrollers  (sprs584j.pdf) 勘误表: TMS320F28030/28031/28032/28033/28034/28035 Piccolo MCU Silicon Errata   (sprz295k.pdf) 应用手册: Digital Peak Current Mode Control With Slope Compensation Using the TMS320F2803x   (sprabe7a.pdf) Software Implementation of PMBus over I2C for TMS320F2803x   (sprabj6.pdf) Piccolo TMS320F2802x/2803x Migration to TMS320F2806x    (sprabj2.pdf) TMS320F280x to TMS320F2802x/TMS320F2803x Migration (sprab40b.pdf) 用户指南: TMS320x2803x Piccolo 增强型局控制器域网 (eCAN)  (zhcu041.pdf) TMS320x2803x Piccolo Local Interconnect Network (LIN) Module Reference Guide (spruge2b.pdf) TMS320x2803x Piccolo System Control and Interrupts Reference Guide (sprugl8c.pdf) TMS320x2803x Piccolo High Resolution Capture (HRCAP) User's Guide (spruh56.pdf) TMS320x2802x, 2803 Piccolo Analog-to-Digital Converter and Comparator Ref. Guide (spruge5f.pdf) TMS320x2802x, 2803 Piccolo High-Resolution Pulse-Width Modulator (HRPWM) RG (spruge8e.pdf) TMS320x2802x, 2803x Piccolo Inter-Integrated Circuit (I2C) Module Ref. Guide (sprufz9d.pdf) TMS320x2802x, 2803x Piccolo Enhanced Pulse Width Modulator (ePWM) Module UG (sprufz9d.pdf) TMS320x2803x Piccolo Control Law Accelerator (CLA) Reference Guide (spruge6b.pdf) TMS320x2802x, 2803x Piccolo Serial Communications Interface (SCI) RG (sprugh1c.pdf) TMS320x2802x, 2803x Piccolo Serial Peripheral Interface (SPI) Reference Guide (sprug71b.pdf) TMS320x2803x Piccolo Boot ROM Reference Guide (sprugo0a.pdf) TMS320x2802x, 2803x Piccolo Enhanced Capture Module (eCAP) Reference Guide (sprufz8a.pdf) TMS320x2803x Piccolo Enhanced Quadrature Encoder Pulse (eQEP) Reference Guide (sprufk8.pdf) TMS320F28035 Piccolo CLA Support for Code Composer Studio v3.3 Release Notes Piccolo Real-time 32-bit Microcontrollers - TMS320F2802x/2803x

  • 2019-03-16
  • 发表了主题帖: 使用555实现定时功能应用实例

    1、555芯片能够实现精确的定时功能。在延时模式下,利用外部的电阻和电容可以实现比较准确的定时。 本例即利用这个功能:程序启动后,按下按键会使555输出一段时间的高电平,小灯也被点亮,同时单片机检测到高电平后,让蜂鸣器发出声音,延时时间结束后,小灯熄灭,蜂鸣器停止鸣叫。 2、在keil c51中新建工程ex65编写如下程序代码,编译并生成ex65hex文件 //555定时器实验, #include <reg51.h> sbit signal = P1^0; sbit BEEP = P3^7; //延时函数 void delayms(unsigned int x) {   unsigned char i;   while(x--)   {     for(i = 0;i < 120;i++);   } } //主函数 void main(void) {   while(1) {    if(signal)    //检测555输出的电平高低   {     BEEP = ~BEEP;   //    delayms(3);   } } }

  • 发表了主题帖: 串口发送和接收字符串实例

    1、在上一实例中,我们利用proteus中自带的虚拟终端实现了单片机通过串口向主机发送字符串的功能。本例中我们加入串口接收字符的功能 2、为了实现单片机通过终端接收字符的功能,我们需要在电路中再加入一路虚拟终端,新加入的这一路虚拟终端连接到单片机的串行接收端口RXD上,在仿真运行后,在该终端输入字符,然后将字符发送到单片机的串行接收端, 3、在keil c51中新建工程ex50,编写如下程序代码,编译并生成ex50.hex文件 // 实例50:利用虚拟中断实现串口数据的发送和接收 // #include <reg51.h>          //包含头文件 //毫秒级延时函数 void delay(unsigned int x) {   unsigned char i; while(x--) {    for(i = 0;i < 120;i++); } } //字符发送函数 void putchar(unsigned char data1)   {   SBUF = data1;               //将待发送的字符送入发送缓冲器 while(TI == 0);            //等待发送完成 TI = 0;                     //发送中断标志请0 } //字符串发送函数 void putstring(unsigned char *dat) {   while(*dat != '\0')           //判断字符串是否发送完毕 {    putchar(*dat);        //发送单个字符   dat++;                 //字符地址加1,指向先下一个字符   delay(5); } } //主函数 void main(void) {   unsigned char c = 0; SCON = 0x50;           //串口方式1 ,允许接收 TMOD = 0x20;           //T1工作于方式2 PCON = 0x00;           //波特率不倍增 TL1 = 0xfd;              //波特率设置 TH1 = 0xfd;            // EA = 1;                    //开总中断 ES = 1;               //开串口接收中断 //TI = 0; TR1 = 1;             //定时器开启 delay(200); putstring("Receiving from 8051...\r\n");         //串口向终端发送字符串,结尾处回车换行 putstring("----------------------\r\n"); delay(50); while(1) {     } } // void revdata(void) interrupt 4 {   unsigned char temp; if(RI == 0) return;         //如果没有接收中断标志,返回 ES = 0;            //关闭串口中断 RI = 0;            //清串行中断标志位 temp = SBUF;        //接收缓冲器中的字符 putchar(temp);        //将接收的字符发送出去 ES = 1;                 //开启串口中断 }

  • 发表了主题帖: I2C总线上接两个AT24C02读写实例

    1、本例对I2C总线上关节两个AT24C02进行读写操作,想将数据0xaa写入第一个at24c02的指定地址,再将该数据读出后存入第二个AT24C02的指定地址,最后读出该数据并送P1口用8位LED显示验证 2、实现方法: 1)两个器件地址的确定 由于第一个AT24C02的3位地址位(A0A1A2)均接地(低电平),第二个AT24C02的三个地址位(A0A1A2)均接电源(高电平),因此第一个AT24C02的地址为000,第二个AT24C02的地址为111.在写命令时,指名要操作的器件地址,即可对不同的AT24C02进行操作 3、在keil c51中新建工程ex54,编写如下程序代码,编译并生成ex54.hex文件 //对I2C总线上挂接多个AT24C02的读写操作 #include <reg51.h>         //  包含51单片机寄存器定义的头文件 #include <intrins.h>       //包含_nop_()函数定义的头文件 #define OP_READ1 0xa1  // 器件1地址以及读取操作,0xa1即为1010 0001B #define OP_WRITE1 0xa0  // 器件1地址以及写入操作,0xa1即为1010 0000B #define OP_READ2 0xaf  // 器件2地址以及读取操作,0xa1即为1010 1111B #define OP_WRITE2 0xae  // 器件2地址以及写入操作,0xa1即为1010 1110B sbit SDA=P3^4;             //将串行数据总线SDA位定义在为P3.4引脚 sbit SCL=P3^3;             //将串行时钟总线SDA位定义在为P3.3引脚 /***************************************************** 函数功能:延时1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒 ***************************************************/ void delay1ms() {    unsigned char i,j;   for(i=0;i<10;i++)    for(j=0;j<33;j++)     ;    } /***************************************************** 函数功能:延时若干毫秒 入口参数:n ***************************************************/ void delaynms(unsigned char n) {    unsigned char i; for(i=0;i<n;i++)     delay1ms(); } /*************************************************** 函数功能:开始数据传送 ***************************************************/ void start() // 开始位 { SDA = 1;    //SDA初始化为高电平“1” SCL = 1;    //开始数据传送时,要求SCL为高电平“1” _nop_();    //等待一个机器周期 _nop_();    //等待一个机器周期 SDA = 0;    //SDA的下降沿被认为是开始信号 _nop_();    //等待一个机器周期 _nop_();    //等待一个机器周期 _nop_();    //等待一个机器周期 _nop_();    //等待一个机器周期 SCL = 0;    //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递) _nop_();    //等待一个机器周期 } /*************************************************** 函数功能:结束数据传送 ***************************************************/ void stop() // 停止位 { SDA = 0;     //SDA初始化为低电平“0” _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 SCL = 1;     //结束数据传送时,要求SCL为高电平“1” _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 _nop_();     //等待一个机器周期 SDA = 1;    //SDA的上升沿被认为是结束信号 } /*************************************************** 函数功能:从AT24Cxx读取数据 出口参数:x ***************************************************/ unsigned char ReadData() // 从AT24Cxx移入数据到MCU { unsigned char i; unsigned char x;           //储存从AT24Cxx中读出的数据 for(i = 0; i < 8; i++) {   SCL = 1;                //SCL置为高电平   x<<=1;                  //将x中的各二进位向左移一位   x|=(unsigned char)SDA;  //将SDA上的数据通过按位“或“运算存入x中   SCL = 0;               //在SCL的下降沿读出数据 } return(x);                //将读取的数据返回 } /*************************************************** 函数功能:向AT24Cxx的当前地址写入数据 入口参数:y (储存待写入的数据) ***************************************************/ //在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0 bit WriteCurrent(unsigned char y) { unsigned char i; bit ack_bit;               //储存应答位 for(i = 0; i < 8; i++)  // 循环移入8个位 {      SDA = (bit)(y&0x80);   //通过按位“与”运算将最高位数据送到S                         //因为传送时高位在前,低位在后   _nop_();              //等待一个机器周期        SCL = 1;              //在SCL的上升沿将数据写入AT24Cxx           _nop_();              //等待一个机器周期    _nop_();               //等待一个机器周期              SCL = 0;              //将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲   y <<= 1;              //将y中的各二进位向左移一位 } SDA = 1;     // 发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,                  //以让SDA线转由接收设备(AT24Cxx)控制 _nop_();        //等待一个机器周期 _nop_();        //等待一个机器周期 SCL = 1;       //根据上述规定,SCL应为高电平 _nop_();       //等待一个机器周期 _nop_();       //等待一个机器周期 _nop_();       //等待一个机器周期 _nop_();       //等待一个机器周期 ack_bit = SDA; //接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节                 //若送高电平,表示没有接收到,传送异常 SCL = 0;       //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递) return  ack_bit;// 返回AT24Cxx应答位 } /*************************************************** 函数功能:向第一个AT24Cxx中的指定地址写入数据 入口参数:add (储存指定的地址);dat(储存待写入的数据) ***************************************************/ void WriteSet1(unsigned char add, unsigned char dat) // 在指定地址addr处写入数据WriteCurrent { start();                  //开始数据传递 WriteCurrent(OP_WRITE1);  //选择要操作的第一个AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(add);        //写入指定地址 WriteCurrent(dat);        //向当前地址(上面指定的地址)写入数据 stop();                   //停止数据传递 delaynms(4);            //1个字节的写入周期为1ms, 最好延时1ms以上 } /*************************************************** 函数功能:向第二个AT24Cxx中的指定地址写入数据 入口参数:add (储存指定的地址);dat(储存待写入的数据) ***************************************************/ void WriteSet2(unsigned char add, unsigned char dat) // 在指定地址addr处写入数据WriteCurrent { start();                  //开始数据传递 WriteCurrent(OP_WRITE2);  //选择要操作的AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(add);        //写入指定地址 WriteCurrent(dat);        //向当前地址(上面指定的地址)写入数据 stop();                   //停止数据传递 delaynms(4);            //1个字节的写入周期为1ms, 最好延时1ms以上 } /*************************************************** 函数功能:从第一个AT24Cxx中的当前地址读取数据 出口参数:x (储存读出的数据) ***************************************************/ unsigned char ReadCurrent1() { unsigned char x; start();                   //开始数据传递 WriteCurrent(OP_READ1);   //选择要操作的第一个AT24Cxx芯片,并告知要读其数据 x=ReadData();             //将读取的数据存入x stop();                   //停止数据传递 return x;                 //返回读取的数据 } /*************************************************** 函数功能:从第二个AT24Cxx中的当前地址读取数据 出口参数:x (储存读出的数据) ***************************************************/ unsigned char ReadCurrent2() { unsigned char x; start();                   //开始数据传递 WriteCurrent(OP_READ2);    //选择要操作的第二个AT24Cxx芯片,并告知要读其数据 x=ReadData();              //将读取的数据存入x stop();                    //停止数据传递 return x;                  //返回读取的数据 } /*************************************************** 函数功能:从第一个AT24Cxx中的指定地址读取数据 入口参数:set_addr 出口参数:x ***************************************************/ unsigned char ReadSet1(unsigned char set_addr) // 在指定地址读取 { start();                      //开始数据传递 WriteCurrent(OP_WRITE1);       //选择要操作的第一个AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(set_addr);       //写入指定地址 return(ReadCurrent1());        //从第一个AT24Cxx芯片指定地址读出数据并返回 } /*************************************************** 函数功能:从第二个AT24Cxx中的指定地址读取数据 入口参数:set_addr 出口参数:x ***************************************************/ unsigned char ReadSet2(unsigned char set_addr) // 在指定地址读取 { start();                       //开始数据传递 WriteCurrent(OP_WRITE2);       //选择要操作的第二个AT24Cxx芯片,并告知要对其写入数据 WriteCurrent(set_addr);        //写入指定地址 return(ReadCurrent2());        //从第二个AT24Cxx芯片指定地址读出数据并返回 } /*************************************************** 函数功能:主函数 ***************************************************/ main(void) {    unsigned char x;    SDA = 1;           // SDA=1,SCL=1,使主从设备处于空闲状态 SCL = 1;             WriteSet1(0x36,0xaa);   //将数据"0xaa"写入第一个AT24C02的指定地址"0x36" x=ReadSet1(0x36);       //从第二个AT24C02中的指定地址"0x36"读出数据      WriteSet2(0x48,x);      //将读出的数据写入第二个AT24C02的指定地址"0x48"? P1=ReadSet2(0x48);      //将从第二个AT24C02的指定地址读出的数据送P1口显示验证 }

  • 发表了主题帖: 8位数据锁存器74HC573应用实例

    1、74HC573是一种8数据锁存器。主要用于数码管、按键等等的控制 。 573有两个功能: 1)数据锁存. 当输入的数据消失时,在芯片的输出端,数据仍然保持; 2) 数据缓冲,加强驱动能力。74LS244/74LS245/74LS373/74LS573都具备数据缓冲的能力。 2、管脚说明: OE:output_enable,输出使能; LE:latch_enable,数据锁存使能,latch是锁存的意思; Dn:第n路输入数据; On:第n路输出数据; 3、工作原理简述 当OE=1是,无论Dn、LE为何,输出端为高阻态; 当OE=0、LE=0时,输出端保持不变; 当OE=0、LE=1时,输出端数据等于输入端数据; 在实际应用的时候是这样做的: a. OE=0; b. 先将数据从单片机的口线上输出到Dn; c. 再将LE从0->1->0 d. 这时,你所需要输出的数据就锁存在On上了,输入的数据在变化也影响不到输出的数据了; 4、在keil c51中新建工程ex56,编写如下程序代码,编译并生成ex56.hex文件 // #include <reg51.h> sbit LE1 = P2^0; sbit OE= P2^1; sbit LE2 = P2^2; void main(void) {   LE1 = 1; OE =0; P3 = 0x55; LE1 = 0; LE2 = 1; OE = 0; P3 =0x11; LE2 = 0; while(1) { } }

  • 发表了主题帖: 单总线温度传感器DS18B20读写实例

    本帖最后由 灞波儿奔 于 2019-3-16 20:59 编辑 1、I2C总线器件与单片机之间的通信需要两根线,而单总线器件与单片机间的数据通信只要一根线。 单总线适用于单主机系统,能够控制一个或多个从机设备。主机通常是单片机,从机可以是单总线器件,他们之间通过一条信号线进行数据交换,单总线上同样允许挂接多个单总线器件,因此,每隔单总线器件必须有各自固定的地址,但总线器件通常需要接一个4.7k左右的上拉电阻,这样,当总线空闲时,状态为高电平。 2、单总线器件的数据操作过程 1)初始化 单总线上的所有处理均从初始化开始,单片机先发送一个复位脉冲,当单总线其间接收到复位脉冲后,先单片机发出应答信号,以便通知单片机:该器件已经准备好等待下一步操作 2)识别单总线器件 总线上允许挂接多个但总线器件,为便于单片机识别,每个单总线器件在出厂前都分配好了64为序列号以作为地址序列码。所以单片机能够根据该序列号来识别和判断对那一个单总线器件进行操作 3)数据交换 单片机与单总线器件之间的数据交换必须遵循严格的通信协议。单总线协议定义了复位信号,应答信号,写/读0,写/读1的集中信号类型,所有的单总线命令都是由这些基本的信号类型组成的,除了应答信号外,其余信号都由单片机发出,并且发送的所有命令和数据都是低位在前,高位在后。 3、DS18B20的工作时序 1)初始化单片机将数据线拉低480-960us后释放,等待15-60us,单总线器件即可输出一个持续时间为60-240us的低电平(应答信号),单片机受到此应答后即可进行后续操作 2)写时序 当主机将数据线的电平从高拉到低时,形成写时序,有写0和写1两种时序。写时序开始后,DS18B20在15-60us期间从数据线上采样,如果采样到低电平,则向DS18B20写0,否则写1,两个独立的时序之间至少需要1us的回复时间按(拉高总线电平) 3)读时序 当主机从DS18B20读取数据时,产生读时序,此时,主机将数据线的电平从高拉到低使读时序被初始化。如果此后15us内,主机在总线上采样到低电平,则从DS18B20读0,否则读1 4、在keil c51中新建工程ex55,编写如下程序代码,编译并生成ex55.hex文件 //DS18B20温度检测及其液晶显示 #include<reg51.h>    //包含单片机寄存器的头文件 #include<intrins.h>  //包含_nop_()函数定义的头文件 unsigned char code digit[10]={"0123456789"};     //定义字符数组显示数字 unsigned char code Str[]={"Test by DS18B20"};    //说明显示的是温度 unsigned char code Error[]={"Error!Check!"};     //说明没有检测到DS18B20 unsigned char code Temp[]={"Temp:"};             //说明显示的是温度 unsigned char code Cent[]={"Cent"};              //温度单位 /******************************************************************************* 以下是对液晶模块的操作程序 *******************************************************************************/ sbit RS=P2^0;           //寄存器选择位,将RS位定义为P2.0引脚 sbit RW=P2^1;           //读写选择位,将RW位定义为P2.1引脚 sbit E=P2^2;            //使能信号位,将E位定义为P2.2引脚 sbit BF=P0^7;           //忙碌标志位,,将BF位定义为P0.7引脚 /***************************************************** 函数功能:延时1ms (3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒 ***************************************************/ void delay1ms() {    unsigned char i,j;   for(i=0;i<10;i++)    for(j=0;j<33;j++)     ;    } /***************************************************** 函数功能:延时若干毫秒 入口参数:n ***************************************************/ void delaynms(unsigned char n) {    unsigned char i; for(i=0;i<n;i++)     delay1ms(); } /***************************************************** 函数功能:判断液晶模块的忙碌状态 返回值:result。result=1,忙碌;result=0,不忙 ***************************************************/ bit BusyTest(void)   {     bit result; RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态     RW=1;     E=1;        //E=1,才允许读写     _nop_();   //空操作     _nop_();     _nop_();     _nop_();   //空操作四个机器周期,给硬件反应时间     result=BF;  //将忙碌标志电平赋给result    E=0;         //将E恢复低电平    return result;   } /***************************************************** 函数功能:将模式设置指令或显示地址写入液晶模块 入口参数:dictate ***************************************************/ void WriteInstruction (unsigned char dictate) {        while(BusyTest()==1);   //如果忙就等待   RS=0;                  //根据规定,RS和R/W同时为低电平时,可以写入指令   RW=0;      E=0;                   //E置低电平(根据表8-6,写指令时,E为高脉冲,                            // 就是让E从0到1发生正跳变,所以应先置"0"   _nop_();   _nop_();               //空操作两个机器周期,给硬件反应时间   P0=dictate;            //将数据送入P0口,即写入指令或地址   _nop_();   _nop_();   _nop_();   _nop_();               //空操作四个机器周期,给硬件反应时间   E=1;                   //E置高电平   _nop_();   _nop_();   _nop_();   _nop_();               //空操作四个机器周期,给硬件反应时间    E=0;                  //当E由高电平跳变成低电平时,液晶模块开始执行命令 } /***************************************************** 函数功能:指定字符显示的实际地址 入口参数:x ***************************************************/ void WriteAddress(unsigned char x) {      WriteInstruction(x|0x80); //显示位置的确定方法规定为"80H+地址码x" } /***************************************************** 函数功能:将数据(字符的标准ASCII码)写入液晶模块 入口参数:y(为字符常量) ***************************************************/ void WriteData(unsigned char y) {     while(BusyTest()==1);      RS=1;           //RS为高电平,RW为低电平时,可以写入数据    RW=0;    E=0;            //E置低电平(根据表8-6,写指令时,E为高脉冲,                      // 就是让E从0到1发生正跳变,所以应先置"0"    P0=y;           //将数据送入P0口,即将数据写入液晶模块    _nop_();    _nop_();     _nop_();      _nop_();       //空操作四个机器周期,给硬件反应时间    E=1;           //E置高电平    _nop_();    _nop_();    _nop_();   _nop_();        //空操作四个机器周期,给硬件反应时间   E=0;            //当E由高电平跳变成低电平时,液晶模块开始执行命令 } /***************************************************** 函数功能:对LCD的显示模式进行初始化设置 ***************************************************/ void LcdInitiate(void) {     delaynms(15);               //延时15ms,首次写指令时应给LCD一段较长的反应时间     WriteInstruction(0x38);     //显示模式设置:16×2显示,5×7点阵,8位数据接口 delaynms(5);                //延时5ms ,给硬件一点反应时间     WriteInstruction(0x38); delaynms(5);               //延时5ms ,给硬件一点反应时间 WriteInstruction(0x38);     //连续三次,确保初始化成功 delaynms(5);               //延时5ms ,给硬件一点反应时间 WriteInstruction(0x0c);     //显示模式设置:显示开,无光标,光标不闪烁 delaynms(5);               //延时5ms ,给硬件一点反应时间 WriteInstruction(0x06);     //显示模式设置:光标右移,字符不移 delaynms(5);                //延时5ms ,给硬件一点反应时间 WriteInstruction(0x01);     //清屏幕指令,将以前的显示内容清除 delaynms(5);             //延时5ms ,给硬件一点反应时间 } /************************************************************************ 以下是DS18B20的操作程序 ************************************************************************/ sbit DQ=P3^3; unsigned char time;   //设置全局变量,专门用于严格延时 /***************************************************** 函数功能:将DS18B20传感器初始化,读取应答信号 出口参数:flag ***************************************************/ bit Init_DS18B20(void) { bit flag;         //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在 DQ = 1;           //先将数据线拉高 for(time=0;time<2;time++) //略微延时约6微秒      ; DQ = 0;           //再将数据线从高拉低,要求保持480~960us for(time=0;time<200;time++)  //略微延时约600微秒      ;         //以向DS18B20发出一持续480~960us的低电平复位脉冲 DQ = 1;           //释放数据线(将数据线拉高)   for(time=0;time<10;time++)      ;  //延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲) flag=DQ;          //让单片机检测是否输出了存在脉冲(DQ=0表示存在)       for(time=0;time<200;time++)  //延时足够长时间,等待存在脉冲输出完毕       ; return (flag);    //返回检测成功标志 } /***************************************************** 函数功能:从DS18B20读取一个字节数据 出口参数:dat ***************************************************/ unsigned char ReadOneChar(void) {   unsigned char i=0;   unsigned char dat;  //储存读出的一个字节数据   for (i=0;i<8;i++)    {            DQ =1;       // 先将数据线拉高      _nop_();     //等待一个机器周期        DQ = 0;      //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序    dat>>=1;      _nop_();     //等待一个机器周期           DQ = 1;     //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备      for(time=0;time<2;time++)              ;      //延时约6us,使主机在15us内采样      if(DQ==1)         dat|=0x80;  //如果读到的数据是1,则将1存入dat    else     dat|=0x00;//如果读到的数据是0,则将0存入dat        //将单片机检测到的电平信号DQ存入r      for(time=0;time<8;time++)          ;              //延时3us,两个读时序之间必须有大于1us的恢复期      }                        return(dat);    //返回读出的十进制数据 } /***************************************************** 函数功能:向DS18B20写入一个字节数据 入口参数:dat ***************************************************/   WriteOneChar(unsigned char dat) { unsigned char i=0; for (i=0; i<8; i++)    {     DQ =1;         // 先将数据线拉高     _nop_();      //等待一个机器周期       DQ=0;          //将数据线从高拉低时即启动写时序           DQ=dat&0x01;   //利用与运算取出要写的某位二进制数据,                        //并将其送到数据线上等待DS18B20采样    for(time=0;time<10;time++)        ;//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样     DQ=1;          //释放数据线           for(time=0;time<1;time++)      ;//延时3us,两个写时序间至少需要1us的恢复期     dat>>=1;       //将dat中的各二进制位数据右移1位    }    for(time=0;time<4;time++)                ; //稍作延时,给硬件一点反应时间 } /****************************************************************************** 以下是与温度有关的显示设置 ******************************************************************************/ /***************************************************** 函数功能:显示没有检测到DS18B20 ***************************************************/    void display_error(void) {        unsigned char i;         WriteAddress(0x00);    //写显示地址,将在第1行第1列开始显示       i = 0;                //从第一个字符开始显示     while(Error != '\0')  //只要没有写到结束标志,就继续写     {            WriteData(Error);   //将字符常量写入LCD      i++;                 //指向下一个字符      delaynms(100);        //延时100ms较长时间,以看清关于显示的说明     }     while(1)              //进入死循环,等待查明原因       ; } /***************************************************** 函数功能:显示说明信息 ***************************************************/    void display_explain(void) {        unsigned char i;         WriteAddress(0x00);    //写显示地址,将在第1行第1列开始显示       i = 0;                //从第一个字符开始显示     while(Str != '\0')  //只要没有写到结束标志,就继续写     {            WriteData(Str);   //将字符常量写入LCD      i++;                 //指向下一个字符      delaynms(100);        //延时100ms较长时间,以看清关于显示的说明     } } /***************************************************** 函数功能:显示温度符号 ***************************************************/    void display_symbol(void) {        unsigned char i;         WriteAddress(0x40);    //写显示地址,将在第2行第1列开始显示       i = 0;                //从第一个字符开始显示     while(Temp != '\0')  //只要没有写到结束标志,就继续写     {            WriteData(Temp);   //将字符常量写入LCD      i++;                 //指向下一个字符      delaynms(50);        //延时1ms给硬件一点反应时间     } } /***************************************************** 函数功能:显示温度的小数点 ***************************************************/    void  display_dot(void) {            WriteAddress(0x49);   //写显示地址,将在第2行第10列开始显示        WriteData('.');      //将小数点的字符常量写入LCD   delaynms(50);         //延时1ms给硬件一点反应时间   } /***************************************************** 函数功能:显示温度的单位(Cent) ***************************************************/    void  display_cent(void) {            unsigned char i;            WriteAddress(0x4c);        //写显示地址,将在第2行第13列开始显示       i = 0;                    //从第一个字符开始显示     while(Cent != '\0')     //只要没有写到结束标志,就继续写     {           WriteData(Cent);     //将字符常量写入LCD      i++;                 //指向下一个字符      delaynms(50);        //延时1ms给硬件一点反应时间     } } /***************************************************** 函数功能:显示温度的整数部分 入口参数:x ***************************************************/ void display_temp1(unsigned char x) { unsigned char j,k,l;     //j,k,l分别储存温度的百位、十位和个位 j=x/100;              //取百位 k=(x%100)/10;    //取十位 l=x%10;             //取个位   WriteAddress(0x46);    //写显示地址,将在第2行第7列开始显示 WriteData(digit[j]);    //将百位数字的字符常量写入LCD WriteData(digit[k]);    //将十位数字的字符常量写入LCD WriteData(digit[l]);    //将个位数字的字符常量写入LCD delaynms(50);         //延时1ms给硬件一点反应时间      } /***************************************************** 函数功能:显示温度的小数数部分 入口参数:x ***************************************************/ void display_temp2(unsigned char x) {   WriteAddress(0x4a);      //写显示地址,将在第2行第11列开始显示 WriteData(digit[x]);     //将小数部分的第一位数字字符常量写入LCD delaynms(50);          //延时1ms给硬件一点反应时间 } /***************************************************** 函数功能:做好读温度的准备 ***************************************************/ void ReadyReadTemp(void) {       Init_DS18B20();     //将DS18B20初始化   WriteOneChar(0xCC); // 跳过读序号列号的操作   WriteOneChar(0x44); // 启动温度转换        for(time=0;time<100;time++)              ;  //温度转换需要一点时间   Init_DS18B20();     //将DS18B20初始化   WriteOneChar(0xCC); //跳过读序号列号的操作   WriteOneChar(0xBE); //读取温度寄存器,前两个分别是温度的低位和高位 } /***************************************************** 函数功能:主函数 ***************************************************/ void main(void) {       unsigned char TL;     //储存暂存器的温度低位      unsigned char TH;    //储存暂存器的温度高位      unsigned char TN;      //储存温度的整数部分    unsigned char TD;       //储存温度的小数部分    LcdInitiate();         //将液晶初始化     delaynms(5);        //延时5ms给硬件一点反应时间   if(Init_DS18B20()==1)      display_error();   display_explain();     display_symbol();    //显示温度说明       display_dot();       //显示温度的小数点       display_cent();      //显示温度的单位    while(1)                //不断检测并显示温度   {   ReadyReadTemp();     //读温度准备     TL=ReadOneChar();    //先读的是温度值低位   TH=ReadOneChar();    //接着读的是温度值高位   TN=TH*16+TL/16;      //实际温度值=(TH*256+TL)/16,即:TH*16+TL/16                      //这样得出的是温度的整数部分,小数部分被丢弃了     TD=(TL%16)*10/16;    //计算温度的小数部分,将余数乘以10再除以16取整,                      //这样得到的是温度小数部分的第一位数字(保留1位小数)     display_temp1(TN);    //显示温度的整数部分     display_temp2(TD);    //显示温度的小数部分       delaynms(10);                    }       }

  • 2019-03-15
  • 发表了主题帖: hx711模块原理图及驱动程序

    驱动程序 unsigned long ReadCount(void) {     unsigned long Count;     unsigned char i;     SCL=0;      //使能AD(PD_SCL 置低)     Count=0;     while(SDA); //AD转换未结束则等待,否则开始读取     for (i = 0; i < 24; i++)     {         SCL = 1;        //PD_SCL 置高(发送脉冲)         Count=Count<<1; //下降沿来时变量Count左移一位,右侧补零         SCL=0;          //PD_SCL 置低         if(SDA) Count++;     }     SCL=1;     Count=Count^0x800000;//第25个脉冲下降沿来时,转换数据     SCL=0;     return(Count); } ---------------------

最近访客

< 1/1 >

统计信息

已有38人来访过

  • 芯币:1082
  • 好友:--
  • 主题:359
  • 回复:23
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言