1950年开始的数字革命将所有现有的机械和模拟电子结构更改为数字计算机。由于数字电子技术的发展呈指数级增长,因此今天,人们几乎不可能抵制使用任何电子设备。从唤醒您的闹钟和为您提供早餐的烤面包机开始,一切都是数字电子的贡献。考虑到所有这些,对我们自己的东西进行编程真的很令人兴奋,这些东西可以完成简单而有用的任务,例如我们将在PIC Microcontroller的此项目中构建的闹钟。我们以前与其他微控制器一起构建了闹钟:
- 使用RTC模块DS1307的Raspberry Pi闹钟
- 带有警报的基于Arduino的数字时钟
- 使用ATmega32单片机的闹钟
该闹钟将具有16x2 LCD显示屏,该显示屏将显示当前时间和设置时间。在需要时,我们将使用几个按钮来设置闹钟时间。使用DS3231 RTC模块将保持当前时间不变,并且我们将使用IIC通信从RTC模块获取这些值。我们已经了解了RTC模块以及如何将其与PIC接口。因此,建议您通读该教程,我们将跳过该教程涵盖的大多数信息。
所需材料:
- 面包板– 2个
- PIC16F877A
- 5V电源-电源模块
- 20 MHz晶体
- 33pf电容器– 2个
- DS3231 RTC模块
- 16 * 2 LCD显示模组
- 10K锅
- 10K和1K电阻
- 按钮– 5个
- 蜂鸣器
- 连接线
先决条件:
这个项目要求您不了解PIC单片机及其编程方法的基础知识。我们将在此项目中使用GPIO,LCD显示和RTC模块。因此最好事先学习如何使用这些模块。以下链接将帮助您学习相同的内容
- 用PIC单片机编写第一个程序
- LCD与PIC接口
- 使用PIC进行I2C通信
- DS3231 RTC与PIC接口
电路原理图:
该基于PIC的闹钟项目的电路图如下所示,该电路图是使用proteus软件创建的。还将在本项目中进一步用于仿真。
五个按钮将用作在所需时间设置警报的输入。因此,所有按钮的一端都接地,而另一端则连接到PORTB引脚,这些引脚上将使用内部上拉电阻以避免引脚浮空。当警报触发并连接到PORT S引脚时,蜂鸣器将作为输出并发出蜂鸣声。PIC通过I2C总线从中接收数据的DS3231 RTC模块始终保持当前时间,因此RTC模块的SCL和SDA引脚连接到PIC控制器的SCL和SDA引脚。 LCD显示器连接到PIC的PORTD,用于显示当前时间和设置时间。在此处了解有关将DS3231 RTC模块与PIC一起使用的更多信息。
完整的电路可以建立在试验板上。由于要连接数十根电线,因此请耐心等待,并确保连接正确。连接完成后,我的硬件设置如下所示
我使用了面包板模块和12V适配器为模块供电。这是我的+ 5V电源电压来源。另外,我必须使用两个面包板来保持电路清洁。如果您要进行更强大的项目,也可以将整个电路焊接到性能板上。
闹钟编程:
此闹钟项目的完整PIC程序可在本页底部找到。该项目还需要三个库,以将LCD,I2C和RTC与PIC一起使用。带有头文件的完整代码可以从此处的ZIP文件下载,并且可以在提取后使用MPLABX打开。在下面,我只是将主c文件解释为小片段。如果您想了解头文件的工作原理,则可以使用上述教程。
在进入主程序之前,我们必须使用更有意义的名称定义使用的引脚。这样,在编程过程中将很容易使用它们。我们程序中定义的引脚如下所示
//定义LCD引脚 #define RS RD2 //定义LCD的复位引脚#define EN RD3 //使能LCD的引脚 #define D4 RD4 // LCD的数据位0 #define D5 RD5 // LCD的数据位1 #define D6 RD6 // LCD的数据位2 #define D7 RD7 // LCD的数据位3 //定义按钮 #define MB RB1 //中间按钮 #define LB RB0 //向左按钮 #define RB RB2 //向右按钮 # define UB RB3 //上按钮 #define BB RB4 //底部按钮 // Define Buzz #define BUZZ RD1 // Buzzer连接到RD1
在 main 函数内部,我们首先声明输入和输出引脚。在我们的项目中,PORTB用于作为输入设备的按钮,因此我们将其引脚设置为输入,而PORTD用于LCD和蜂鸣器,因此我们将其引脚设置为Output。另外,引脚永远不要悬空,这意味着I / O引脚应始终连接到地或+ 5V电压。在我们的按钮情况下,当不按下按钮时,引脚将不会连接到任何东西,因此我们使用了内部上拉电阻,该电阻在不使用时将引脚设置为高电平。使用控制寄存器完成此操作,如下所示
TRISD = 0x00; //使端口d引脚作为outptu用于LCD接口 TRISB = 0xFF的; //的Switch被声明为输入管脚 OPTION_REG = 0b00000000; //为开关BUZZ = 0启用端口B的上拉电阻;//蜂鸣器转
由于我们将LCD和I2C头文件与主程序链接在一起,因此可以通过调用一个简单的函数来启动LCD初始化。I2C初始化也可以执行相同的操作。由于RTC模块以100kHz工作,因此我们在这里以100kHz开始I2C通信。
Lcd_Start(); //初始化LCD模块 I2C_Initialize(100); //使用100KHz时钟初始化I2C主设备
以下功能用于在RTC模块上设置时间和日期,一旦设置了时间和日期,请删除该行。否则,每次启动程序时,都会一次又一次地设置时间和日期
一旦时间和日期//移除下面行中的被设置为第一个时间。 Set_Time_Date(); //在RTC模块上设置时间和日期
为了表明程序正在启动,我们将显示一个小的介绍屏幕,其中显示项目名称和网站名称,如下所示
//在LCD上显示介绍性消息 Lcd_Clear(); Lcd_Set_Cursor(1,1); Lcd_Print_String(“闹钟”); Lcd_Set_Cursor(2,1); Lcd_Print_String(“ -Circuit Digest”); __delay_ms(1500);
接下来,在 while 循环中,我们需要从RTC模块读取当前时间和日期,只需调用以下函数即可完成此操作。
Update_Current_Date_Time(); //从RTC模块读取当前日期和时间
调用上述函数将用当前值更新变量sec,min和hour。为了在LCD屏幕上显示它们, 我们必须使用下面的代码将它们拆分为单个字符。
//将其拆分为char以显示在LCD上 char sec_0 = sec%10; char sec_1 =(秒/ 10); char min_0 = min%10; char min_1 =分钟/ 10; char hour_0 =小时%10; char hour_1 = hour / 10;
接下来,我们在LCD屏幕上更新值。当前时间将显示在第一行中,而必须触发警报的设置时间将显示在第二行中。相同的代码如下所示。
//在液晶屏上显示当前时间 Lcd_Clear(); Lcd_Set_Cursor(1,1); Lcd_Print_String(“ TIME:”); Lcd_Print_Char(hour_1 +'0'); Lcd_Print_Char(hour_0 +'0'); Lcd_Print_Char(':'); Lcd_Print_Char(min_1 +'0'); Lcd_Print_Char(min_0 +'0'); Lcd_Print_Char(':'); Lcd_Print_Char(sec_1 +'0'); Lcd_Print_Char(sec_0 +'0'); //在液晶屏上显示日期 Lcd_Set_Cursor(2,1); Lcd_Print_String(“ Alarm:”); Lcd_Print_Char(alarm_val +'0'); Lcd_Print_Char(alarm_val +'0'); Lcd_Print_Char(':'); Lcd_Print_Char(alarm_val +'0'); Lcd_Print_Char(alarm_val +'0');
现在,我们已经在LCD上显示了时间和设置时间,我们必须检查用户是否正在尝试设置闹钟时间。为此,用户必须按下中间按钮,因此我们将检查中间按钮是否被按下,并切换变量以进入警报设置模式。将再次按下同一按钮以确认已设置值,在这种情况下,我们必须退出警报设置模式。因此,我们使用下面的代码行来更改变量 set_alarm 的状态。
//使用中间按钮检查是否必须设置警报(MB == 0 && set_alarm == 0){//如果在(!MB)时按下了中间按钮并且未打开警报, //等到释放按钮set_alarm = 1; // //开始设置警报值} if(MB == 0 && set_alarm == 1){//如果在(!MB)时按下了中间按钮并且未关闭警报, //等到释放按钮set_alarm = 0; //停止设置警报值}
如果用户按下中间按钮,则表示他正在尝试设置闹钟时间。在这种情况下,程序将使用上面的代码进入警报设置模式。在警报设置模式下,如果用户按下向左或向右按钮,则意味着我们必须向左或向右移动光标。要做到这一点,我们只需增加递减光标必须放置的位置的值
if(LB == 0){//如果 在(!LB)时 按下了左键;//等待按钮被释放pos--; // //然后将光标向左移动 } if(RB == 0){//如果 在(!RB)时 按下了右键 //等待按钮pos ++ 释放;//将光标移到右侧}
在微控制器或微处理器上使用按钮时,要解决一个普遍的问题。此问题称为开关弹跳。就是说,当按下按钮时,它可能会给MCU / MPU带来嘈杂的脉冲,这可能会使MCU伪造多个条目。通过在开关两端增加一个电容器或在检测到按钮按下后立即使用延迟功能可以解决此问题。这种解决方案称为反跳。在这里,我们使用了 while 循环将程序保持在适当位置,直到释放按钮为止。这不是最佳的反跳解决方案,但对我们而言,它会很好地工作。
而(!RB);
与左右按钮类似,我们也有上下按钮,可用于增加或减少警报时间的值。执行以下操作的代码如下所示。注意,设置的警报时间的每个字符都由数组的索引值寻址。这是我们可以轻松访问必须更改其值的所需字符。
if(UB == 0){//如果 在(!UB)时 按下上按钮 //等待直到按钮被释放alarm_val ++; //增加特定的字符值 }, 如果(BB == 0){//如果 在(!UB)时 按下了下按钮;//等待直到按钮被释放alarm_val--; //降低特定的char值 }
设置闹钟时间后,用户将再次按下中间按钮。然后,我们可以开始比较当前时间和设置的时间。通过检查当前时间的每个字符是否等于设置时间的字符来进行比较。如果值相等,则通过设置 trigger_alarm 变量来触发警报,否则我们将进行比较直到其相等。
//如果设置了闹铃,请检查设置值是否等于当前值 (set_alarm == 0 && alarm_val == hour_1 && alarm_val == hour_0 && alarm_val == min_1 && alarm_val == min_0) trigger_alarm = 1; //如果值匹配则打开触发器
如果设置了警报,我们必须发出蜂鸣声以提醒用户警报。只需按如下所示的规则间隔切换蜂鸣器即可完成此操作。
if(trigger_alarm){//如果触发了警报 //蜂鸣器 BUZZ = 1; __delay_ms(500); BUZZ = 0; __delay_ms(500); }
模拟:
该程序也可以使用proteus软件进行模拟。只需重新创建上面显示的电路并将十六进制文件加载到PIC中即可。可以在此处链接的ZIP文件中找到此项目的十六进制代码。模拟过程中拍摄的屏幕截图如下所示
当您尝试向项目中添加新功能时,模拟将变得非常有用。您还可以使用I2C调试器模块检查通过I2C总线输入和输出的数据。您可以尝试按按钮,也可以设置闹钟时间。当设置的时间等于当前时间时,蜂鸣器将变高。
使用PIC16F877A的数字闹钟的工作:
在试验板上建立电路,从下载链接中获取代码,并使用MplabX和XC8编译器对其进行编译。如果您已从此处提供的ZIP文件中下载了代码,则由于头文件已附加,因此编译它应该没有问题。
编译后,使用PicKit3编程器将程序上传到您的硬件。电路图中还显示了将Pickit编程器连接至PIC IC的连接。程序上传后,您应该会看到简介屏幕,然后显示时间,然后可以使用按钮设置闹钟时间。通电时我的硬件设置如下所示。
当闹铃时间与当前时间匹配时,蜂鸣器将开始蜂鸣以警告用户。完整的工作可以在下面的视频中找到。该项目有很多可供选择的选择。RTC模块可以跟踪任何时间和日期,因此您可以在所需的任何时间/日期执行计划任务。您还可以连接风扇或照明灯等交流电设备,并在需要时安排将其打开或关闭。您可以在这个项目上建立更多的东西,让我知道您对这个项目的升级有什么想法,我很高兴收到您的来信。
希望您理解该项目并从该过程中学到一些有用的东西。如果您对此项目有任何疑问,请使用评论部分进行发布或使用论坛获取任何技术帮助。
带有头文件的完整PIC代码可在此处找到