单片机

单片机(Microcontroller, MCU)是一种集成了计算机功能的微型计算机,通常由一个微处理器(CPU)、存储器(ROM、RAM)、输入/输出接口、定时器/计数器等功能模块集成在同一芯片上。单片机是一种常用于嵌入式系统中的控制器,它被广泛应用于家电、汽车、工业自动化、医疗设备、消费电子、物联网(IoT)设备等多个领域。

单片机晶振旁边两个对地电容叫晶振的负载电容,分别接在晶振的两个脚上和对地的电容,一般在几十皮发。它会影响到晶振的谐振频率和输出幅度,一般订购晶振时候供货方会问你负载电容是多少。

一般单片机的晶振工作于并联谐振状态,也可以理解为谐振电容的一部分。它是根据晶振厂家提供的晶振要求负载电容选值的,换句话说,晶振的频率就是在它提供的负载电容下测得的,能最大限度的保证频率值的误差。也能保证温漂等误差。两个电容的取值都是相同的,或者说相差不大,如果相差太大,容易造成谐振的不平衡,容易造成停振或者干脆不起振。晶振负载电容值指的是晶振的交流电路中参与振荡与晶振串联或者并联的负载电容值。晶振的电路频率主要是有晶振自身决定,既然负载电容参与电路振荡,肯定会对频率多少起到微调作用。负载电容值越小,振荡电路就会反而越高。

各种逻辑芯片的晶振引脚可以等效为电容三点式振荡器。晶振引脚的内部通常是一个反相器,或者是奇数个反相器串联。在晶振输出引脚XO和晶振输入引脚XI之间用一个电阻连接,对于CMOS芯片通常是数M到数十M欧之间。很多芯片的引脚内部已经包含了这个电阻,引脚外部就不用接了。这个电阻是为了使反相器在振荡初始时处与线性状态,反相器就如同一个有很大增益的放大器,以便于起振。石英晶体也连接在晶振引脚的输入和输出之间,等效为一个并联谐振回路,振荡频率应该是石英晶体的并联谐振频率。晶体旁边的两个电容接地,实际上就是电容三点式电路的分压电容,接地点就是分压点。以接地点即分压点为参考点,振荡引脚的输入和输出是反相的,但从并联谐振回路即石英晶体两端来看,形成一个正反馈以保证电路持续振荡。在芯片设计时,这两个电容就已经形成了,一般是两个的容量相等,容量大小依工艺和版图而不同,但终归是比较小,不一定适合很宽的频率范围。外接时大约是数PF到数十PF,依频率和石英晶体的特性而定。需要注意的是:这两个电容串联的值是并联在谐振回路上的,会影响振荡频率。当两个电容量相等时,反馈系数是0.5,一般是可以满足振荡条件的,但如果不易起振或振荡不稳定可以减小输入端对地电容量,而增加输出端的值以提高反馈量。

单片机晶振的负载电容常见问题分析

振荡电路不匹配导致晶振不起振,影响振荡电路的三个指标:频率误差、负性阻抗、激励电平。

①频率误差太大,导致实际频率偏移标称频率从而引起晶振不起振。

解决办法:选择合适的PPM值的产品。

②负性阻抗过大太小都会导致晶振不起振。晶振在工作逐渐出现停振现象,用手碰触或者用电烙铁加热晶振引脚又开始工作。

解决办法:负性阻抗过大,可以将晶振外接电容Cd和Cg的值调大来降低负性阻抗;负性阻抗太小,则可以将晶振外接电容Cd和Cg的值调小来增大负性阻抗。一般而言,负性阻抗值应满足不少于晶振标称最大阻抗3-5倍。

③激励电平过大或者过小也将会导致晶振不起振,激励电平过大则可能出现晶振在工作中发烫,逐渐出现停振现象。

解决办法:通过调整电路中的Rd的大小来调节振荡电路对晶振输出的激励电平。一般而言,激励电平越小越好,处理功耗低之外,还跟振荡电路的稳定性和晶振的使用寿命有关。

晶振PCB布线:在PCB布线时,晶振电路的走线尽可能的短直,并尽可能靠近MCU,尽量降低振荡电路中的杂散电容对晶振的影响;PCB布线的时候,尽量不要在晶振下面走信号线,避免对晶振产生电磁干扰,从而导致振荡电路不稳定。带有晶振的电路板一般不建议用超声波清洗,避免发生共振而损坏晶振导致不良。

在PCB上的位置:如果你的PCB板比较大,晶振尽量靠边一些,这是因为晶振设计在中间位置会因PCB板变形产生的机械张力而受影响,可能出现不良;如果你的PCB板比较小,晶振位置尽量往中间靠,不要设计在边沿位置,这是因为PCB板小,一般SMT过回流焊都是多拼板,在分板的时候产生的机械张力会对晶振有影响,可能产生不良。

转自:电子发烧友网

围观 568

单片机是嵌入式系统的核心元件,使用单片机的电路要复杂得多,但在更改和添加新功能时,带有单片机的电路更加容易实现,这也正是电器设备使用单片机的原因。那么在单片机电路的设计中需要注意的难点有哪些?你都解决了吗?下面分享10个单片机电路设计中的难点,一起来学习吧~

一、单片机上拉电阻的选择

10 个单片机电路设计中的难点,你都解决了吗?

10 个单片机电路设计中的难点,你都解决了吗?

大家可以看到复位电路中电阻R1=10k时RST是高电平 ,而当R1=50时RST为低电平,很明显R1=10k时是错误的,单片机一直处在复位状态时根本无法工作。出现这样的原因是由于RST引脚内含三极管,即便在截止状态时也会有少量截止电流,当R取的非常大时,微弱的截止电流通过就产生了高电平。

二、LED串联电阻的计算问题

通常红色贴片LED:电压1.6V-2.4V,电流2-20mA,在2-5mA亮度有所变化,5mA以上亮度基本无变化。

10 个单片机电路设计中的难点,你都解决了吗?

三、端口出现不够用的情况

这时可以借助扩展芯片来实现,比如三八译码器74HC138来拓展。

10 个单片机电路设计中的难点,你都解决了吗?

10 个单片机电路设计中的难点,你都解决了吗?

四、滤波电容

滤波电容分为高频滤波电容和低频滤波电容。

1、高频滤波电容一般用104容(0.1uF),目的是短路高频分量,保护器件免受高频干扰。普通的IC(集成)器件的电源与地之间都要加,去除高频干扰(空气静电)。

2、低频滤波电容一般用电解电容(100uF),目的是去除低频纹波,存储一部分能量,稳定电源。大多接在电源接口处,大功率元器件旁边,如:USB借口,步进电机、1602背光显示。耐压值至少高于系统最高电压的2倍。

五、三极管的作用

1、开关作用:

10 个单片机电路设计中的难点,你都解决了吗?

LEDS6为高电平时截止,为低电平时导通。
限流电阻的计算:集电极电流为I,则基极电流为I/100(这里涉及到放大作用,集电极电流是基极的100倍),PN结电压0.7V,R=(5-0.7)/(I/100)

2、放大作用:集电极电流是基极电流的100倍

3、电平转换:

10 个单片机电路设计中的难点,你都解决了吗?

当基极为高电平时,三极管导通,右侧的导线接地为低电平,当基极为低电平时,三极管截止,输出高电平。

六、数码管的相关问题

10 个单片机电路设计中的难点,你都解决了吗?

数码管点亮形成的数字由a,b,c,d,e,f,e,dp(小数点)构成,字模及真值表如上图。

七、电流电压驱动问题

由于单片机输出有限,当负载很多的时候需要另外加驱动芯片 ,比如74HC245。

八、上拉电阻

上拉电阻选取原则

1、从节约功耗及芯片灌电流能力考虑应当足够大;电阻大,电流小。
2、从确保足够的驱动电流考虑应当足够小;电阻小,电流大。
3、对于高速电路,过大的上拉电阻可能会导致边沿变平缓。
综合考虑:上拉电阻常用值在1K到10K之间选取,下拉同理。

上下拉电阻,上拉就是将不确定的信号通过一个电阻嵌位在高电平,下拉同理。
1、电平转换,提高输出电平参数值。
2、OC门必须加上拉电阻才能使用。
3、加大普通IO引脚驱动能力。
4、悬空引脚上下拉抗干扰。

九、晶振和复位电路

晶振电路

1、晶振选择:
根据实际系统需求选择,6M,12M,11.0592M,20M等待。
2、负载电容:
对地接2个10到30pF的电容即可,常用20pF。
3、万用表测晶振:
直接用红表笔对晶振引脚,黑表笔接GND,测量电压即可。

复位电路

把单片机内部电路设置成为一个确定的状态,所有的寄存器初始化。
51单片机的复位时间大约在2个机械周期左右,具体需要看芯片数据手册。
一般通过复位芯片或者复位电路,具体的阻容参数的计算,通过google查找。

十、按键抖动及消除

按键也是机械装置,在按下或放开的一瞬间会产生抖动,如下图:

10 个单片机电路设计中的难点,你都解决了吗?

消除方法有两种:软件除抖和硬件除抖,其中硬件除抖是应用了电容对高频信号短路的原理。

软件除抖是检测出键闭合后执行一个延时程序,产生5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。

来源:电源网订阅号

围观 350

一个单片机硬件系统的硬件电路设计包含两部分内容:
一是系统扩展,即单片机内部的功能单元,如ROM、RAM、I/O、定时器/计数器、中断系统等不能满足应用系统的要求时,必须在片外进行扩展,选择适当的芯片,设计相应的电路。
二是系统的配置,即按照系统功能要求配置外围设备,如键盘、显示器、打印机、A/D、D/A转换器等,要设计合适的接口电路。

系统的扩展和配置应遵循以下原则:

1、尽可能选择典型电路,并符合单片机硬件系统常规用法。为硬件系统的标准化、模块化打下良好的基础。

2、系统扩展与外围设备的配置水平应充分满足应用系统的功能要求,并留有适当余地,以便进行二次开发。

3、硬件结构应结合应用软件方案一并考虑。硬件结构与软件方案会产生相互影响,考虑的原则是:软件能实现的功能尽可能由软件实现,以简化硬件结构。但必须注意,由软件实现的硬件功能,一般响应时间比硬件实现长,且占用CPU时间。

4、单片机硬件系统中的相关器件要尽可能做到性能匹配。 如选用CMOS芯片单片机硬件系统构成低功耗系统时,系统中所有芯片都应尽可能选择低功耗产品。

5、可靠性及抗干扰设计是硬件设计必不可少的一部分,它包括芯片、器件选择、去耦滤波、印刷电路板布线、通道隔离等。

6、单片机硬件系统外围电路较多时,必须考虑其驱动能力。驱动能力不足时,系统工作不可靠,可通过增设线驱动器增强驱动能力或减少芯片功耗来降低总线负载。

7、尽量朝“单片”方向设计硬件系统。系统器件越多,器件之间相互干扰也越强,功耗也增大,也不可避免地降低了系统的稳定性。随着单片机片内集成的功能越来越强,真正的片上系统SoC已经可以实现,如ST公司推出的μPSD32××系列产品在一块芯片上集成了80C32核、大容量FLASH存储器、SRAM、A/D、I/O、两个串口、看门狗、上电复位电路等等。

本文转自:电子发烧友 - 电子说,转载此文目的在于传递更多信息,版权归原作者所有。

围观 480

本文设计的是一款基于单片机的红外智能空调遥控器,这种遥控器能采用测量脉冲宽度的方法学习红外信号,同时使用游程编码算法对数据进行压缩后存储,并利用单片机内部定时器PWM模式产生红外载波,成功实现了对红外遥控的学习与再现。

下面请看详细设计流程。

系统总体结构与硬件设计

系统采用模块化设计,各模块通过接口电路与主控芯片相连。主要模块有:矩阵键盘,液晶显示,存储模块,红外发送模块,红外接收模块,RS232、RS485 通信模块,以及温度检测模块。系统结构图如图1 所示。

系统以Atmega16 单片机作为主控芯片,Atmega16具有16K 字节的系统内可编程Flash ,512 字节EEPROM,1K 字节SRAM,32 个通用I/O 口线,32 个通用工作寄存器,用于边界扫描的JTAG 接口,支持片内调试与编程,三个具有比较模式的灵活的定时器/计数器(T/C),片内/外中断,可编程串行USART,有起始条件检测器的通用串行接口,8 路10 位具有可选差分输入级可编程增益的ADC,具有片内振荡器的可编程看门狗定时器,一个SPI 串行端口,以及六个可以通过软件进行选择的省电模式。该芯片功能强大,满足系统设计需要并提供了充分的扩展空间。主控芯片使用8MHz 的晶振,晶振电路靠近主控芯片,尽量减少输入噪声。复位电路采用低电平复位。

智能学习型红外空调遥控器的设计与实现
图1 系统结构图

矩阵键盘采用3*3 的设计,设置了8 个功能键,方便用户进行手动操作。其中单独设计了一颗模式切换键,可在学习、发射、通信模式中切换。为了实现学习功能, 红外接收模块使用了一体化接收头NB1838,其光电检测和前置放大器集成于同一封装,中心频率为37.9KHz. NB1838 的环氧树脂封装结构为其提供了一个特殊的红外滤光器,对自然光和电场干扰有很强的防护性。NB1838 对接收到的红外信号进行放大、检波、整形,并调制出红外编码,得到TTL 波形,反相后输入单片机,再由单片机进行进一步的处理,存储到EEPROM 中,接收电路如图2 所示。

智能学习型红外空调遥控器的设计与实现
图2 接收硬件电路图。

考虑到系统需要的存储空间比较大,设计了单独的存储模块,选用的EEPROM 是AT24C64,它提供了8KB 的容量,通过IIC 协议与Atmega16 TWI 接口通信,将学习到的红外指令存储在此,掉电不丢失。

在发射模式下,系统从EEPROM 读取相应数据信息,利用三极管9013 组成的放大电路,通过大功率红外发射管将调制好的红外信号发射出去。发射电路如图3所示,非发送状态时,三极管工作在截止状态,红外发射管不工作,有利于降低功耗以及延长红外发射管的使用寿命。经实际测试,发射距离可达到10m 左右。

智能学习型红外空调遥控器的设计与实现
图3 发射硬件电路图。

通信模式中,系统通过RS232 电路与上位机通信,在与上位机通信时使用DS18B20 反馈温度信息,DS18B20 一线总线设计大大提高了系统的抗干扰性,独特而且经济。系统还增加了RS485 模块,便于组网,以实现对多个红外设备进行控制。RS485 在组网时只需要用一对双绞线将子设备的"A"、"B"端连接起来,这种接线方式为总线式拓扑结构,在同一总线上可挂接多个结点,连接方便。

为了增加设备的实用性,系统设计了两个电源方案,一个是直接接入5V 直流电源,一个是接入12V直流电源,然后通过L7805 构成的变压电路降压为5V使用。

系统软件设计与实现

系统程序主要分为三个部分:学习模式,发送模式以及通信模式。当第一次进入系统时,初始化设置设备地址,然后设置通信的波特率,提供1200、9600 以及19200 三种选择。系统主程序即在三个模式间切换,默认进入通信模式,可以通过模式切换按键改变模式,也可以通过上位机直接更改。出于系统的稳定性需要,在程序中加入了软件看门狗,防止程序"跑飞".

学习功能设计

学习模式

红外遥控器的码型多样,编码一般包括:帧头、系统码、操作码、同步码、帧间隔码、帧尾,且同步码与帧间隔码出现的位置不固定,因此码型格式灵活多变,很难区分各种码型的编码含义;各个红外遥控的编码长度不尽相同,发送方式也多种多样,最常用的有三种:完整帧只发送一次、完整帧重复发送两次、先发送一个完整帧,后重复发送帧头和一个脉冲。面对如此多样化的编码方式,如果区分每种编码的含义进行学习,学习的复杂度将会很高,并且通用性也会受到影响。所以,为了避开各色码型的干扰,系统在学习时并不关心码型数据的实际意义,只记录脉冲的时间宽度。系统主要针对载波频率为38KHz(周期为26us)的红外遥控器,利用变量IR_time 记录接收到的脉冲宽度。学习程序流程如图4 所示。

智能学习型红外空调遥控器的设计与实现
图4 学习程序流程图。

压缩存储

由于不考虑具体的码型数据意义,只记录脉冲的宽度,系统的学习功能通用性得到了提高,但这种方式学习到的数据量很大,对存储的要求就变得很高。

尽管系统针对存储的大容量需求设计了单独的存储模块,但考虑到应在不增加硬件开销的情况下保证足够的存储容量,以及满足未来扩展的需要,在进行数据存储时,采取了数据压缩技术。

从学习到的电平数据可以发现,无论数据是1 还是0,都有相同时长的电平出现,这符合游程编码的特点。游程编码是一种简单的非破坏性资料压缩法,其好处是加压缩和解压缩都非常快,其方法是计算连续出现的资料长度压缩之。比如:一张二值图像的数据为:

WWWWWWWWBWWWWBBBWWWWWWWBWWWWW

使用游程编码压缩可得:8W1B4W3B7W1B 5W.

可见,压缩效率极高,且可避免复杂的编码和解码运算。所以,在存储时,系统对学习到的数据进行游程编码压缩[7,8].例如,学习到的一组空调遥控器的数据为[157 153 23 53 … 23 53 23 180 156 152 23 53 …53 23],如图5 所示,对重复的电平数据采用游程编码压缩后,原本需要199 字节的空调遥控码,只需要106个字节即可存储,压缩率达53.27%.因此,在存储时针对学习到的数据特点采取游程编码压缩,可以有效节约存储空间。

智能学习型红外空调遥控器的设计与实现
图5 一组典型的空调数据帧。

发射功能设计

现有的红外遥控器很多都是采用外部电路产生载波信号,例如使用NEC555 振荡器产生载波信号。为了减少硬件开销,本系统使用单片机内部的定时器产生载波。系统使用的是Atmega16 单片机,其定时器功能强大,具有普通模式、CTC 模式、快速PWM 模式、相位修正PWM 模式等工作模式,系统利用定时器1,使其工作在快速PWM 模式,产生占空比为1:3 的38KHz 的PWM 波。当发送某条指令时,单片机从对应的EEPROM 中提取指令信息,然后调制到生成的载波上,再通过发射电路即可完成红外信号的发射。

通信功能设计

上位机通信

本遥控器除了能通过功能按键实现手动操作外,还可以通过上位机软件对遥控器进行控制。遥控器与上位机通过RS232 模块进行通信,首先配置上位机软件,确定串口号,选择与设备相同的波特率及主从设备地址,然后根据需要选择相应的指令,点击发送即可通过上位机对设备进行控制。由于本遥控器是基于空调遥控器进行研究的,在与上位机通信时,系统中的温度检测模块会上传实时温度,便于用户进行调整。图6 为上位机软件流程图。

智能学习型红外空调遥控器的设计与实现
图6 上位机软件流程图。

组网控制

为了实现对多个设备的联网控制,还设计了RS485 模块。各子遥控器通过RS485 模块的"A"、"B"端连接在一起,组成控制网络,如图7 所示,其中一个作为主遥控器,与上位机通过RS232 模块进行串口通信。当上位机需要对某个子设备进行控制时,选择相应的子设备地址号,发送指令即可,主遥控器收到指令信息后,会将指令发给对应的子设备。与主遥控器相连的上位机PC 连接Internet,作为本地服务器,可实现远程控制。

用户登录远程客户端,经身份验证后与服务器建立连接,可发送指令给本地服务器,本地服务器再经过串口通信对遥控器进行相应操作。如果遥控器主机与上位机距离较远,RS232 不能满足通信需要,也可不使用遥控器主机,在上位机PC 上使用RS232-485 转接头,通过RS485 直接将遥控器网络与PC 机485 接口相连,利用上位机对遥控器网络直接进行控制。

智能学习型红外空调遥控器的设计与实现
图7 控制网络示意图。

本文设计了一款智能空调遥控器。该系统采用只记录红外信号脉冲宽度,不考虑红外编码格式的方式,通过游程编码算法将红外信号压缩后保存到EEPROM 中,并直接利用主控芯片定时器的PWM 模式产生38KHz 的载波,节约了硬件成本,除手动操作外还可以通过上位机对遥控器进行控制,使用方便。

系统成功实现了对多种空调遥控器的学习与功能再现,操作灵活,性能稳定。本系统还可用于智能家居中,对不同的红外设备进行控制,也可用于远程网络控制,为智能家居及远程监控提供了一种实现方法。

来源:中国百科网

围观 372

如何实现单片机用一个I/O采集多个按键信号

使用模数转换(ADC)的特点就可以实现单片机用一个I/O采集多个按键信号。

一、单片机的I/O口检测按键简说

我们知道,一般情况下单片机的一个I/O口作为普通I/O口的话,只能检测识别一个按键。

日常设计中,如果碰到按键数量较多的话,会采用行列式键盘,例如最常见的4X4矩阵键盘,这样可以实现用8个I/O口检测16个按键。

如何实现单片机用一个I/O采集多个按键信号?

还有就是键盘接口,典型的是我们计算机上用的键盘,其采用PS/2接口,现在一般计算机上用的是USB接口的键盘。

另外还有使用串口或者IIC、SPI接口的键盘芯片,这些使用常见的串口、IIC、SPI通信协议实现。

但是这些都一个以上的I/O口,不是真正的用多个按键。

如何实现单片机用一个I/O采集多个按键信号?

那么有没有更简单的办法,使用更少的I/O口资源检测更多的按键呢?

二、基于模数转换的AD键盘

我们知道按键检测实际上是检测连接按键的端口的高低电平值,在单个I/O口检测单个按键时,只是简单的判断连接按键的端口的电平是高电平(+5V)还是低电平(0V)。那么是否可以通过电平的微小变化来检测按键是否被按下呢?

下图为一个A/D键盘的原理图,从图中可以看出,当不同的按键被按下时,ADC端点处的电压不同,通过判断不同的电压值就可以判断出是那个按键被按下。

如何实现单片机用一个I/O采集多个按键信号?

对于具有AD转换功能的单片机来说,直接接到一个AD通道即可。对于没有AD转换功能的单片机,可采用一个AD转换模块。

对于这种按键,有以下缺点:

1、对于同一点处的电压值,A/D多次采样的结果不可能完全相同。

2)、电阻的误差。电阻值由于电阻的精度和环境温度的原因,误差较大,所以A/D键盘各个按键点的分压不准确。

3)、为尽量减少误差,可以采取增加电阻精度、增加温度补偿等方法,另外在软件处理时候要注意消除按键抖动等因素,还要对实际转换值和标准值给出误差补偿。

4)、如果按键按下,经过A/D转换,若实际转换值在允许误差范围之内(需要实际测量各点电压,并计算各点电压平均值),则认为按键按下,否则程序不响应。

5)、实际试验过程中,还要考虑电阻的累积误差,选用精度越高的电阻,可分辨的按键数目越多。

转自:畅学单片机

围观 545

时间片轮询法,在很多书籍中有提到,而且有很多时候都是与操作系统一起出现,也就是说很多时候是操作系统中使用了这一方法。不过我们这里要说的这个时间片轮询法并不是挂在操作系统下,而是在前后台程序中使用此法。也是本文要详细说明和介绍的方法。

对于时间片轮询法,虽然有不少书籍都有介绍,但大多说得并不系统,只是提提概念而已。下面本人将详细介绍本人模式,并参考别人的代码建立的一个时间片轮询架构程序的方法,我想将给初学者有一定的借鉴性。

这里我们先介绍一下定时器的复用功能。

使用1个定时器,可以是任意的定时器,这里不做特殊说明,下面假设有3个任务,那么我们应该做如下工作:

1. 初始化定时器,这里假设定时器的定时中断为1ms(当然你可以改成10ms,这个和操作系统一样,中断过于频繁效率就低,中断太长,实时性差)。

2. 定义一个数值:

代码:
#define TASK_NUM (3) // 这里定义的任务数为3,表示有三个任务会使用此定时器定时。
uint16 TaskCount[TASK_NUM] ; // 这里为三个任务定义三个变量来存放定时值
uint8 TaskMark[TASK_NUM]; // 同样对应三个标志位,为0表示时间没到,为1表示定时时间到。

3. 在定时器中断服务函数中添加:

代码:
void TimerInterrupt(void)
{
uint8 i;

for (i=0; i
{
if (TaskCount[i])
{
TaskCount[i]--;
if (TaskCount[i] == 0)
{
TaskMark[i] = 0x01;
}
}
}
}

代码解释:定时中断服务函数,在中断中逐个判断,如果定时值为0了,表示没有使用此定时器或此定时器已经完成定时,不着处理。否则定时器减一,知道为零时,相应标志位值1,表示此任务的定时值到了。

4. 在我们的应用程序中,在需要的应用定时的地方添加如下代码,下面就以任务1为例:

代码:
TaskCount[0] = 20; // 延时20ms
TaskMark[0] = 0x00; // 启动此任务的定时器

到此我们只需要在任务中判断TaskMark[0] 是否为0x01即可。其他任务添加相同,至此一个定时器的复用问题就实现了。用需要的朋友可以试试,效果不错哦。

通过上面对1个定时器的复用我们可以看出,在等待一个定时的到来的同时我们可以循环判断标志位,同时也可以去执行其他函数。

循环判断标志位:
那么我们可以想想,如果循环判断标志位,是不是就和上面介绍的顺序执行程序是一样的呢?一个大循环,只是这个延时比普通的for循环精确一些,可以实现精确延时。

执行其他函数:
那么如果我们在一个函数延时的时候去执行其他函数,充分利用CPU时间,是不是和操作系统有些类似了呢?但是操作系统的任务管理和切换是非常复杂的。

下面我们就将利用此方法架构一直新的应用程序。

时间片轮询法的架构:

1.设计一个结构体:

代码:
// 任务结构
typedef struct _TASK_COMPONENTS
{
uint8 Run; // 程序运行标记:0-不运行,1运行
uint8 Timer; // 计时器
uint8 ItvTime; // 任务运行间隔时间
void (*TaskHook)(void); // 要运行的任务函数
} TASK_COMPONENTS; // 任务定义

这个结构体的设计非常重要,一个用4个参数,注释说的非常详细,这里不在描述。

2. 任务运行标志出来,此函数就相当于中断服务函数,需要在定时器的中断服务函数中调用此函数,这里独立出来,并于移植和理解。

代码:
void TaskRemarks(void)
{
uint8 i;
for (i=0; i// 逐个任务时间处理
{
if (TaskComps[i].Timer) // 时间不为0
{
TaskComps[i].Timer--; // 减去一个节拍
if (TaskComps[i].Timer == 0) // 时间减完了
{
TaskComps[i].Timer = TaskComps[i].ItvTime; // 恢复计时器值,从新下一次
TaskComps[i].Run = 1; // 任务可以运行
}
}
}
}

大家认真对比一下次函数,和上面定时复用的函数是不是一样的呢?

3. 任务处理

代码:
void TaskProcess(void)
{
uint8 i;
for (i=0; i// 逐个任务时间处理
{
if (TaskComps[i].Run) // 时间不为0
{
TaskComps[i].TaskHook(); // 运行任务
TaskComps[i].Run = 0; // 标志清0
}
}
}

此函数就是判断什么时候该执行那一个任务了,实现任务的管理操作,应用者只需要在main()函数中调用此函数就可以了,并不需要去分别调用和处理任务函数。

到此,一个时间片轮询应用程序的架构就建好了,大家看看是不是非常简单呢?此架构只需要两个函数,一个结构体,为了应用方面下面将再建立一个枚举型变量。

下面我就就说说怎样应用吧,假设我们有三个任务:时钟显示,按键扫描,和工作状态显示。

1. 定义一个上面定义的那种结构体变量

代码:

static TASK_COMPONENTS TaskComps[] =
{
{0, 60, 60, TaskDisplayClock}, // 显示时钟
{0, 20, 20, TaskKeySan}, // 按键扫描
{0, 30, 30, TaskDispStatus}, // 显示工作状态
// 这里添加你的任务。。。。
};

来源:网络

围观 421

1 系统设计原理

步进电机控制系统主要由单片机、键盘LED、驱动/放大和PC上位机等4个模块组成,其中PC机模块是软件控制部分。为保护单片机控制系统硬件电路,在单片机和步进电机之间增加过流保护电路。图l为步进电机控制系统框图。

5分钟实现单片机步进电机控制设计
图1 步进电机控制系统框图

2 系统硬件电路设计

2.1 单片机模块

单片机模块主要由MSP430FG4618单片机及外围滤波、电源管理和晶振等电路组成。

MSP430FG4618单片机内部的8 KB RAM和116 KB Flash满足控制系统的存储要求,P1和P2端口在步进电机工作过程中根据按键状态判断是否跳入中断服务程序来改变步进电机的工作状态,USART模块实现单片机和PC上位机之间的通信,实现PC机对步进电机控制。

5分钟实现单片机步进电机控制设计
图2 单片机模块设计结构框图

2.2 键盘/LED模块

为实现人机对话,该系统设计扩展了3x4按钮矩阵键盘和4片8段LED数码管,可手动直接操作该控制系统。

系统上电后,通过键盘输入步进电机的启停、步数转速和转向等,由LED管动态显示步进电机的转速和转向。键盘的输入和LED管的输出由8279进行控制,减少单片机工作负担。

8279编程工作在键盘扫描输入方式,读入键盘时具有去抖动功能,避免误触发。图3为键盘LED模块设计结构框图。

5分钟实现单片机步进电机控制设计
图3 键盘LED模块框图

2.3 驱动/放大模块

控制系统采用步进电机控制用的脉冲分配器(又称逻辑转换器)PMM8713,该器件是CMOS集成电路,相输出驱动能力(源电流或吸入电源)为20 mA,适用于控制三相或四相步进电机,可选择下列6种激励方式:三相步进电进:1相,2相,1-2相;四相步进电进:1相,2相,1-2相。输入方式可选择单时钟(加方向信号)和双时钟(正转或反转时钟)两种方式,具有正反转控制、初始化复位、原点监视、激励方式监视和输入脉冲监视等功能。

5分钟实现单片机步进电机控制设计
图4 驱动放大电路

3 系统软件设计

3.1 单片机程序

利用单片机的定时器TIMER_A(TA)中断产生脉冲信号,通过在响应的中断程序中实现步进电机步数和圈数的准确计数,通过PWM实现转速控制;利用P1.0端口的中断关闭TA中断程序,并推入堆栈,停止电机;P1.1中断则开启TA中断,堆栈推入程序计数器(PC),开启电机;P3.1端口输出高电平由PMM8713的U/D端口控制电机的转向;P3.0~P3.7端口接8279的8个数据接口,当单片机扫描到矩阵键盘有键按下时,利用P2端口的中断设置TA,控制启停、调速和转向等,同时单片机反馈给8279控制LED管显示转速和转向。其程序流程如图5所示。

5分钟实现单片机步进电机控制设计
图5 单片机程序流程

3.2 PC上位机模块

PC上位机模块实现PC机对步进电机的控制。利用MSP430单片机的USART模块实现与PC上位机的通信,PC机通过串口向单片机发送控制命令,实现电机控制。

单片机所接收到控制命令暂存在RXBUFFER中,然后与存储在片内Flash的中断程序的入口地址相比较,相同就进入中断,实现步进电机的控制。操作该模块时需要开启8 MHz晶振为USART模块设置波特率(设置波特率为9 600)。

控制软件由VB6.0编写,利用MSComm控件实现串行通讯功能。其控制软件界面如图6所示。

5分钟实现单片机步进电机控制设计
图6 控制软件界面

4 系统检测

为检验该控制系统的实际工作情况,在给定PMM2101输出工作电流的状态下采用能量转化法测得步进电机输出的最大静转矩。选取输出电流间隔0.2 A,测到步进电机最大静转矩与电流之间关系的静特性曲线,如图7所示,说明该控制系统设计较合理。

5分钟实现单片机步进电机控制设计
静特性曲线

来源:电子发烧友网

围观 681

在三位数字中,从左至右的第一、第二位为有效数字,第三位表示前两位数字乘10的N次方(单位为Ω)。如果阻值中有小数点,则用“R”表示,并占一位有效数字。例如:标示为“103”的阻值为10&TImes;10=10kΩ;标示为“222”的阻值为2200Ω即2.2kΩ;标示为“105”的阻值为1MΩ。需要注意的是,要将这种标示法与一般的数字表示方法区别开来,如标示为220的电阻器阻值为22Ω,只有标志为221的电阻器阻值才为220Ω。

标示为“0”或…000”的排阻阻值为OΩ,这种排阻实际上是跳线(短路线)。
一些精密排阻采用四位数字加一个字母的标示方法(或者只有四位数字)。前三位数字分别表示阻值的百位、十位、个位数字,第四位数字表示前面三个数字乘10的N次方,单位为欧姆;数字后面的第一个英文字母代表误差(G=2%、F=1%、D=0.25%、B=O.1%、A或W=0.05%、Q=0.02%、T=0.01%、V=0.005%)。如标示为“2341”的排阻的电阻为234&TImes;10=2340Ω。

一文看懂单片机排阻的作用

排阻的作用

内存芯片下方均匀分布的“芝麻粒”,实际上是位于内存颗粒和金手指之间的“排阻”。排阻,是一排电阻的简称。我们知道,内存在处理、传输数据时会产生大小不一的工作电流。而在内存颗粒走线的必经之处安装一排电阻,则能够帮助内存起到稳压作用,让内存工作更稳定。从而提升内存的稳定性,增强内存使用寿命。而你说的内存右边角上的“小绿豆”。我们一般称之为SPD。SPD是一存储体,它存储了厂商对内存的详细配置信息:如内存的工作电压,位宽,操作时序等。每次开机后自检时,系统都会首先读取内存SPD中的相关信息,来自动配置硬件资源,以避免出错。上拉、限流。和普通电阻一样,相比而言简化了PCB的设计、安装,减小空间,保证焊接质量。

排阻引脚说明

一文看懂单片机排阻的作用

1与a2与b3与c4与d之间的电阻都是10欧,与其它的管脚没有任何关系.就是一排电阻,做在了一个原件上.

有的还有一个公脚,就是为了方便使用,拿万用表量一下就会发现所有脚对公共脚的阻值均是标称值,除公共脚外其它任意两脚阻值是标称值的两倍,很明显任意两脚通过公共脚脚串联的嘛!用在有很多上下拉电阻的场合应用特方便,比如并行通讯线上,还节省空间。

51单片机最小系统排阻作用

起上拉作用:

上拉就是将不确定的信号通过一个电阻嵌位在高电平,电阻同时起限流作用,下拉同理。上拉是对器件注入电流,下拉是输出电流,弱强只是上拉电阻的阻值不同,没有什么严格区分,对于非集电极(或漏极)开路输出型电路(如普通门电路)提升电流和电压的能力是有限的,上拉电阻的功能主要是为集电极开路输出型电路输出电流通道。

另外其他I/O口都是准双向口且都有驱动能力,P0口也是准双向口但是驱动能力小,加排阻说白了就是给P0加驱动电路,电源通过排阻向P0口供电,使其能够驱动与P0口相连的元件。

一文看懂单片机排阻的作用

单片机中排阻的焊接方法

先找出排阻的公共端。公共端在排阻标有小白点的一侧。也可以用万用表电阻档测量一下,任意选择一端,测量该端与其余引脚的电阻,若个引脚的电阻相等,该端为公共端,否则,另一端为公共端。公共端连接单片机电源,其它引脚分别连接单片机IO口。具体焊接方法与焊接普通电阻一样,只是引脚多一点而已。可先焊接两端,定位后,再焊接中间引脚。

如图:带点的一端为排阻的公共端。

一文看懂单片机排阻的作用

RP是排阻,J0就接数码管的段码。把排阻有字的一面对着自己,最左端有一个圆点右或方点,对应的管脚就是公共脚了。

一文看懂单片机排阻的作用

来源:网络

围观 321

很多单片机项目恐怕都是没有操作系统的前后台结构,就是main函数里用while无限循环各种任务,中断处理紧急任务。这种结构最简单,上手很容易,可是当项目比较大时,这种结构就不那么适合了,编写代码前你必须非常小心的设计各个模块和全局变量,否则最终会使整个代码结构杂乱无序,不利于维护,而且往往会因为修改了某部分代码而莫名其妙的影响到其他功能,而使调试陷入困境。

改变其中局面的最有效措施当然是引入嵌入式操作系统,但是大多数的操作系统都是付费的(特别是商业项目)。我们熟悉的uc-os/II如果你应用于非商业项目它是免费的,而应用于商业项目的话则要付费,而且价格不菲。

我们也可以自己编写一套嵌入式OS,这当然最好了。可要编写一套完整的OS并非易事,而且当项目并不是非常复杂的话也不需要一个完整的os支持。我们只要用到OS最基本的任务调度和上下文切换就够了。正是基于这样的想法,最近的一个项目中我就尝试采用事件驱动的思想重新构建了代码架构,实际使用的效果还不错,在这里做个总结。

本质上新架构仍然是前后台结构,只不过原来的函数直接调用改成通过指向函数的指针来调用。实际上这也是嵌入式OS任务调度的一个核心。

C语言中可以定义指向函数的指针:
void (*handle)(void);

这里的handle就是一个指向函数的指针,我们只要将某函数的函数名赋给该指针,就能通过实现函数的调用了:

void func1(void)
{
// Code
}
handle = func1;
(*handle)(); // 实现func1的调用

有了这个函数调用新方法,我们就可以想办法将某个事件与某个函数关联,实现所谓的事件驱动。例如,按键1按下就是一个事件,func1响应按键1按下事件。但是,如果是单纯的调用方法替代又有什么意义呢?这又怎么会是事件驱动呢?关键就在于使用函数指针调用方法可以使模块和模块之间的耦合度将到最低。一个例子来说明这个问题,一个按键检测模块用于检测按键,一个电源模块处理按键1动作。

传统的前后台处理方法:
main.c

void main()
{
...
while(1)
{
...
keyScan();
if(flagKeyPress)
{
keyHandle(); // 检测到按键就设置flagKeyPress标志,进入处理函数
}
}
}

key.c

void keyHandle(void)
{
switch (_keyName) // 存放按键值的全局变量
{
...
case KEY1: pwrOpen(); break;
case KEY2: pwrClose(); break;
}
}

power.c

void pwrOpen(void)
{
...
}
void pwrClose(void)
{
...
}

这样的结构的缺点在哪里呢?

1. key代码中直接涉及到power代码的函数,如果power代码里的函数变更,将引起key代码的变更

2. 一个按键值对应一个处理函数,如果要增加响应处理函数就要再次修改key代码

3. 当项目越来越大时,引入的全局变量会越来越多,占用过多的内存

很显然key模块与其他模块的耦合程度太高了,修改其他模块的代码都势必去修改key代码。理想的状态是key模块只负责检测按键,并触发一个按键事件,至于这个按键被哪个模块处理,它压根不需要知道,大大减少模块之间的耦合度,也减少出错的几率。这不正好是事件驱动的思想吗?

接下来,该如何实现呢?

事件驱动的实现

需要一个事件队列:

u16 _event[MAX_EVENT_QUEUE];

它是一个循环队列,保存事件编号,我们可以用一个16位数为各种事件编号,可以定义65535个事件足够使用了。

一个处理函数队列:

typedef struct
{
u16 event; // 事件编号
void (*handle)(void); // 处理函数
}handleType;

handleType _handle[MAX_HANDLE_QUEUE];

它实际是一个数组,每个元素保存事件编号和对应的处理函数指针。

一个驱动函数:

void eventProc(void)
{
u16 event;
u8 i;
if((_eventHead!=_eventTail) || _eventFull) // 事件队列里有事件时才处理
{
event = _eq[_eventHead];
_event [_eventHead++] = 0; // 清除事件

if(_eventHead>= MAX_EVENT_QUEUE)
{
_eventHead= 0; // 循环队列
}
// 依次比较,执行与事件编号相对应的函数
for(i=0; i<_handleTail; i++)
{
if(_handle[i].event == event)
{
(*_handle[i].handle)();
}
}
}
}

main函数可以精简成这样:

void main(void)
{
...
while(1)
{
eventProc();
}
}

这样代码的缺陷是需要为处理函数队列分配一个非常大的内存空间,项目越复杂分配的空间越大,显然不可取。需要做个变通,减少内存空间的使用量。

改进与变通

这样代码的缺陷是需要为处理函数队列分配一个非常大的内存空间,项目越复杂分配的空间越大,显然不可取。需要做个变通,减少内存空间的使用量。

typedef struct
{
void (*handle)(u16 event); // 仅保存模块总的散转函数
}handleType;
handleType _handle[MAX_HANDLE_QUEUE];

修改驱动函数:

void eventProc(void)
{
u16 event;
u8 i;
if((_eventHead!=_eventTail) || _eventFull) // 事件队列里有事件时才处理
{
...
for(i=0; i<_handleTail; i++)
{
(*_handle[i].handle)(event); // 将事件编号传递给模块散转函数
}
}
}

把散转处理交回给各模块,例如power模块的散转函数:

void pwrEventHandle(u16 event)
{
switch (event)
{
...
case EVENT_KEY1_PRESS: pwrOpen(); break;
...
}
}

在power模块的初始化函数中,将该散转函数加入到处理函数队列中:
// 该函数在系统初始化时调用
void pwrInit(void)
{
...
addEventListener(pwrEventHandle);
...
}

addEventListener定义如下:
void addEventListener(void (*pFunc)(u16 event))
{
if(!_handleFull)
{
_handle[_handleTail].handle = pFunc;
_handleTail++;

if(_handleTail>= MAX_HANDLE_QUEUE)
{
_handleFull= TRUE;
}
}
}

每个模块都定义各自的散转处理,然后在初始化的时候将该函数存入处理事件队列中,即能实现事件处理又不会占用很多的内存空间。

加入到事件队列需要封装成统一的函数dispatchEven,由各模块直接调用。例如,key模块就可以dispatchEvent(EVENT_KEY1_PRESS)来触发一个事件

void dispatchEvent(u16 event)
{
u8 i;
bool canDispatch;
canDispatch = TRUE;
if(!_eventFull)
{
// 为了避免同一事件被多次加入到事件队列中
for(i=_eventHead; i!=_eventTail;)
{
if(_event[i] == event)
{
canDispatch = FALSE;
break;
}
i++;
if(i >= MAX_EVENT_QUEUE)
{
i = 0;
}
}
if(canDispatch)
{
_event[_eventTail++] = event;
if(_eventTail>= MAX_EVENT_QUEUE)
{
_eventTail= 0;
}
if(_eventTail== _eventHead)
{
_eventFull = TRUE;
}
}
}
}

深一步:针对与时间相关的事件

对于与时间相关的事件(循环事件和延时处理事件)需要做进一步处理。

首先要设定系统Tick,可以用一个定时器来生成,例如配置一个定时器,每10ms中断一次。
注:tick一般指os的kernel计时单位,用于处理定时、延时事件之类。一般使用硬件定时器中断处理tick事件

定义一个时间事件队列:

typedef struct
{
u8 type; // 事件类别,循环事件还是延时事件
u16 event; // 触发的事件编号
u16 timer; // 延时或周期时间计数器
u16 timerBackup; // 用于周期事件的时间计数备份
}timerEventType;
timerEventType _timerEvent[MAX_TIMER_EVENT_Q];

在定时器Tick中断中将时间事件转换成系统事件:

void SysTickHandler(void)
{
...
for(i=0; i<_timerEventTail; i++)
{
_timerEvent[i].timer--;
if(_timerEvent[i].timer == 0)
{
dispatchEvent(_timerEvent[i].event);// 事件触发器

if(_timerEvent[i].type == CYCLE_EVENT)
{
// 循环事件,重新计数
_timerEvent[i].timer = _timerEvent[i].timerBackup;
}
else
{
// 延时事件,触发后删除
delTimerEvent(_timerEvent[i].event);
}
}
}
}

将增加和删除时间事件封装成函数,便以调用:

void addTimerEvent(u8 type, u16 event, u16 timer)
{
_timerEvent[_timerEventTail].type = type;
_timerEvent[_timerEventTail].event = event;
_timerEvent[_timerEventTail].timer = timer; // 时间单位是系统Tick间隔时间
_timerEvent[_timerEventTail].timerBackup = timer; // 延时事件并不使用
_timerEventTail++;
}

void delTimerEvent(u16 event)
{
...
for(i=0; i<_timerEventTail; i++)
{
if(_timerEvent[i].event == event)
{
for(j=i; j<_timerEventTail; j++)
{
_timerEvent[j] = _timerEvent[j+1];
}
_timerEventFull= FALSE;
_timerEventTail--;
}
}
}

对于延时处理,用事件驱动的方法并不理想,因为这可能需要将一段完整的代码拆成两个函数,破坏了代码的完整性。解决的方法需要采用OS的上下文切换,这就涉及到程序堆栈问题,用纯C代码不容易实现。

——【感谢】资料来源于https://wenku.baidu.com/view/5465391d10a6f524ccbf8591.html

转自:Engraver

围观 435

1、前言

本文设计了一款针对空调设备的智能学习型红外遥控器,采用记录脉冲宽度的方法,成功实现了对多种红外空调遥控信号的学习与再现,真正实现了"万能"。本文在阐述了系统的总体结构及硬件设计的基础上,详细研究了系统学习,发送及通信功能的软件设计与实现。

2、系统总体结构与硬件设计

系统采用模块化设计,各模块通过接口电路与主控芯片相连。主要模块有:矩阵键盘,液晶显示,存储模块,红外发送模块,红外接收模块,RS232、RS485 通信模块,以及温度检测模块。

系统以Atmega16 单片机作为主控芯片,Atmega16具有16K 字节的系统内可编程Flash ,512 字节EEPROM,1K 字节SRAM,32 个通用I/O 口线,32 个通用工作寄存器,用于边界扫描的JTAG 接口,支持片内调试与编程,三个具有比较模式的灵活的定时器/计数器(T/C),片内/外中断,可编程串行USART,有起始条件检测器的通用串行接口,8 路10 位具有可选差分输入级可编程增益的ADC,具有片内振荡器的可编程看门狗定时器,一个SPI 串行端口,以及六个可以通过软件进行选择的省电模式。该芯片功能强大,满足系统设计需要并提供了充分的扩展空间。主控芯片使用8MHz 的晶振,晶振电路靠近主控芯片,尽量减少输入噪声。复位电路采用低电平复位。
矩阵键盘采用3*3 的设计,设置了8 个功能键,方便用户进行手动操作。其中单独设计了一颗模式切换键,可在学习、发射、通信模式中切换。为了实现学习功能, 红外接收模块使用了一体化接收头NB1838,其光电检测和前置放大器集成于同一封装,中心频率为37.9KHz. NB1838 的环氧树脂封装结构为其提供了一个特殊的红外滤光器,对自然光和电场干扰有很强的防护性。NB1838 对接收到的红外信号进行放大、检波、整形,并调制出红外编码,得到TTL 波形,反相后输入单片机,再由单片机进行进一步的处理,存储到EEPROM 中。

考虑到系统需要的存储空间比较大,设计了单独的存储模块,选用的EEPROM 是AT24C64,它提供了8KB 的容量,通过IIC 协议与Atmega16 TWI 接口通信,将学习到的红外指令存储在此,掉电不丢失。
在发射模式下,系统从EEPROM 读取相应数据信息,利用三极管9013 组成的放大电路,通过大功率红外发射管将调制好的红外信号发射出去。发射电路如图3所示,非发送状态时,三极管工作在截止状态,红外发射管不工作,有利于降低功耗以及延长红外发射管的使用寿命。经实际测试,发射距离可达到10m 左右。

通信模式中,系统通过RS232 电路与上位机通信,在与上位机通信时使用DS18B20 反馈温度信息,DS18B20 一线总线设计大大提高了系统的抗干扰性,独特而且经济。系统还增加了RS485 模块,便于组网,以实现对多个红外设备进行控制。RS485 在组网时只需要用一对双绞线将子设备的"A"、"B"端连接起来,这种接线方式为总线式拓扑结构,在同一总线上可挂接多个结点,连接方便。

为了增加设备的实用性,系统设计了两个电源方案,一个是直接接入5V 直流电源,一个是接入12V直流电源,然后通过L7805 构成的变压电路降压为5V使用。

3、系统软件设计与实现

系统程序主要分为三个部分:学习模式,发送模式以及通信模式。当第一次进入系统时,初始化设置设备地址,然后设置通信的波特率,提供1200、9600 以及19200 三种选择。系统主程序即在三个模式间切换,默认进入通信模式,可以通过模式切换按键改变模式,也可以通过上位机直接更改。出于系统的稳定性需要,在程序中加入了软件看门狗,防止程序"跑飞".

3.1 学习功能设计

3.1.1 学习模式

红外遥控器的码型多样,编码一般包括:帧头、系统码、操作码、同步码、帧间隔码、帧尾,且同步码与帧间隔码出现的位置不固定,因此码型格式灵活多变,很难区分各种码型的编码含义;各个红外遥控的编码长度不尽相同,发送方式也多种多样,最常用的有三种:完整帧只发送一次、完整帧重复发送两次、先发送一个完整帧,后重复发送帧头和一个脉冲。面对如此多样化的编码方式,如果区分每种编码的含义进行学习,学习的复杂度将会很高,并且通用性也会受到影响。所以,为了避开各色码型的干扰,系统在学习时并不关心码型数据的实际意义,只记录脉冲的时间宽度。系统主要针对载波频率为38KHz(周期为26us)的红外遥控器,利用变量IR_time 记录接收到的脉冲宽度。

3.1.2 压缩存储

由于不考虑具体的码型数据意义,只记录脉冲的宽度,系统的学习功能通用性得到了提高,但这种方式学习到的数据量很大,对存储的要求就变得很高。

尽管系统针对存储的大容量需求设计了单独的存储模块,但考虑到应在不增加硬件开销的情况下保证足够的存储容量,以及满足未来扩展的需要,在进行数据存储时,采取了数据压缩技术。

从学习到的电平数据可以发现,无论数据是1 还是0,都有相同时长的电平出现,这符合游程编码的特点。游程编码是一种简单的非破坏性资料压缩法,其好处是加压缩和解压缩都非常快,其方法是计算连续出现的资料长度压缩之。比如:一张二值图像的数据为:

WWWWWWWWBWWWWBBBWWWWWWWBWWWWW

使用游程编码压缩可得:8W1B4W3B7W1B 5W.

可见,压缩效率极高,且可避免复杂的编码和解码运算。所以,在存储时,系统对学习到的数据进行游程编码压缩[7,8].例如,学习到的一组空调遥控器的数据为[157 153 23 53 … 23 53 23 180 156 152 23 53 …53 23],如图5 所示,对重复的电平数据采用游程编码压缩后,原本需要199 字节的空调遥控码,只需要106个字节即可存储,压缩率达53.27%.因此,在存储时针对学习到的数据特点采取游程编码压缩,可以有效节约存储空间。

3.2 发射功能设计

现有的红外遥控器很多都是采用外部电路产生载波信号,例如使用NEC555 振荡器产生载波信号。为了减少硬件开销,本系统使用单片机内部的定时器产生载波。系统使用的是Atmega16 单片机,其定时器功能强大,具有普通模式、CTC 模式、快速PWM 模式、相位修正PWM 模式等工作模式,系统利用定时器1,使其工作在快速PWM 模式,产生占空比为1:3 的38KHz 的PWM 波。当发送某条指令时,单片机从对应的EEPROM 中提取指令信息,然后调制到生成的载波上,再通过发射电路即可完成红外信号的发射。

3.3 通信功能设计

3.3.1 上位机通信

本遥控器除了能通过功能按键实现手动操作外,还可以通过上位机软件对遥控器进行控制。遥控器与上位机通过RS232 模块进行通信,首先配置上位机软件,确定串口号,选择与设备相同的波特率及主从设备地址,然后根据需要选择相应的指令,点击发送即可通过上位机对设备进行控制。由于本遥控器是基于空调遥控器进行研究的,在与上位机通信时,系统中的温度检测模块会上传实时温度,便于用户进行调整。

3.3.2 组网控制

为了实现对多个设备的联网控制,还设计了RS485 模块。各子遥控器通过RS485 模块的"A"、"B"端连接在一起,组成控制网络,如图7 所示,其中一个作为主遥控器,与上位机通过RS232 模块进行串口通信。当上位机需要对某个子设备进行控制时,选择相应的子设备地址号,发送指令即可,主遥控器收到指令信息后,会将指令发给对应的子设备。与主遥控器相连的上位机PC 连接Internet,作为本地服务器,可实现远程控制。

用户登录远程客户端,经身份验证后与服务器建立连接,可发送指令给本地服务器,本地服务器再经过串口通信对遥控器进行相应操作。如果遥控器主机与上位机距离较远,RS232 不能满足通信需要,也可不使用遥控器主机,在上位机PC 上使用RS232-485 转接头,通过RS485 直接将遥控器网络与PC 机485 接口相连,利用上位机对遥控器网络直接进行控制。

4、结语

本文设计了一款智能空调遥控器。该系统采用只记录红外信号脉冲宽度,不考虑红外编码格式的方式,通过游程编码算法将红外信号压缩后保存到EEPROM 中,并直接利用主控芯片定时器的PWM 模式产生38KHz 的载波,节约了硬件成本,除手动操作外还可以通过上位机对遥控器进行控制,使用方便。
系统成功实现了对多种空调遥控器的学习与功能再现,操作灵活,性能稳定。本系统还可用于智能家居中,对不同的红外设备进行控制,也可用于远程网络控制,为智能家居及远程监控提供了一种实现方法。

来源:51单片机设计

围观 502

页面

订阅 RSS - 单片机