赞
踩
以前在单片机学习中,驱动数码管是直接将数码管连接单片机,在单片机上需要编写很复杂的程序,并且对数码管需要实时扫描。在实际项目开发中,一般使用数码管专用控制芯片来驱动数码管,提高开发效率。
TM1640是一种LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU数字接口、数据锁存器、LED驱动等电路。主要应用于电子产品LED显示屏驱动。采用SOP28封装形式。TM1640只是数码管驱动芯片的一种,还有很多其他的驱动芯片,具体芯片选择需要根据项目需求及预算来决定。
可驱动16位数码管。
有共阴极与共阳极两种电路图,根据开发板上的数码管实际连接来决定使用哪个。
这两个引脚分别与单片机的两个GPIO相连。
8位数码管
这八个共阴极的LED灯相当于一个数码管,可以当成一个数码管来处理。
#include "stm32f10x.h" #include "sys.h" #include "delay.h" #include "rtc.h" #include "TM1640.h" int main (void) { u8 c=0x01; RCC_Configuration(); //系统时钟初始化 RTC_Config(); //RTC初始化 TM1640_Init(); //TM1640初始化 while(1) { if(RTC_Get()==0) //读出RTC时间 { TM1640_display(0,rday/10); //天 TM1640_display(1,rday%10+10); //+10是为了显示后面的小数点 TM1640_display(2,rhour/10); //时 TM1640_display(3,rhour%10+10); TM1640_display(4,rmin/10); //分 TM1640_display(5,rmin%10+10); TM1640_display(6,rsec/10); //秒 TM1640_display(7,rsec%10); TM1640_led(c); //与TM1640连接的8个LED全亮 c<<=1; //数据左移 流水灯 if(c==0x00)c=0x01; //8个灯显示完后重新开始 delay_ms(125); //延时 } } }
#include "TM1640.h" #include "delay.h" #define DEL 1 //宏定义 通信速率(默认为1,如不能通信可加大数值) //地址模式的设置 //#define TM1640MEDO_ADD 0x40 //宏定义 自动加一模式 #define TM1640MEDO_ADD 0x44 //宏定义 固定地址模式(推荐) //显示亮度的设置 //#define TM1640MEDO_DISPLAY 0x88 //宏定义 亮度 最小 //#define TM1640MEDO_DISPLAY 0x89 //宏定义 亮度 //#define TM1640MEDO_DISPLAY 0x8a //宏定义 亮度 //#define TM1640MEDO_DISPLAY 0x8b //宏定义 亮度 #define TM1640MEDO_DISPLAY 0x8c //宏定义 亮度(推荐) //#define TM1640MEDO_DISPLAY 0x8d //宏定义 亮度 //#define TM1640MEDO_DISPLAY 0x8f //宏定义 亮度 最大 #define TM1640MEDO_DISPLAY_OFF 0x80 //宏定义 亮度 关 //通信时序 启始(基础GPIO操作)(低层) void TM1640_start() { GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1 GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1 delay_us(DEL); GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0 delay_us(DEL); GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0 delay_us(DEL); } //通信时序 结束(基础GPIO操作)(低层) void TM1640_stop() { GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0 GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1 delay_us(DEL); GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1 delay_us(DEL); } //写数据(低层) void TM1640_write(u8 date) { u8 i; u8 aa; aa=date; GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0 GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0 for(i=0;i<8;i++) { GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0 delay_us(DEL); if(aa&0x01) { GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1 delay_us(DEL); } else { GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0 delay_us(DEL); } GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1 delay_us(DEL); aa=aa>>1; } GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0 GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0 } //TM1640接口初始化 void TM1640_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = TM1640_DIN | TM1640_SCLK; //选择端口号(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz) GPIO_Init(TM1640_GPIOPORT, &GPIO_InitStructure); GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1 GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1 TM1640_start(); TM1640_write(TM1640MEDO_ADD); //设置数据,0x40,0x44分别对应地址自动加一和固定地址模式 TM1640_stop(); TM1640_start(); TM1640_write(TM1640MEDO_DISPLAY); //控制显示,开显示,0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f分别对应脉冲宽度为: //------------------1/16, 2/16, 4/16, 10/16, 11/16, 12/16, 13/16, 14/16 //0x80关显示 TM1640_stop(); } //固定地址模式的显示输出8个LED控制 void TM1640_led(u8 date) { TM1640_start(); TM1640_write(TM1640_LEDPORT); //传显示数据对应的地址 TM1640_write(date); //传1BYTE显示数据 TM1640_stop(); } //固定地址模式的显示输出 void TM1640_display(u8 address,u8 date) { const u8 buff[21]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x00};//数字0~9及0~9加点显示段码表 //--------------- 0 1 2 3 4 5 6 7 8 9 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 无 TM1640_start(); TM1640_write(0xC0+address); //传显示数据对应的地址 TM1640_write(buff[date]); //传1BYTE显示数据 TM1640_stop(); } //地址自动加一模式的显示输出 void TM1640_display_add(u8 address,u8 date) { u8 i; const u8 buff[21]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x00};//数字0~9及0~9加点显示段码表 //--------------- 0 1 2 3 4 5 6 7 8 9 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 无 TM1640_start(); TM1640_write(0xC0+address); //设置起始地址 for(i=0;i<16;i++) { TM1640_write(buff[date]); } TM1640_stop(); }
#ifndef __TM1640_H #define __TM1640_H #include "sys.h" #define TM1640_GPIOPORT GPIOA //定义IO接口 #define TM1640_DIN GPIO_Pin_12 //定义IO接口 #define TM1640_SCLK GPIO_Pin_11 //定义IO接口 #define TM1640_LEDPORT 0xC8 //定义IO接口 void TM1640_Init(void);//初始化 void TM1640_led(u8 date);// void TM1640_display(u8 address,u8 date);// void TM1640_display_add(u8 address,u8 date);// #endif
/* //时间读写与设置说明// 1,在mani函数开头放入RTC_Config();就可以使能时钟了。 在RTC_Config();函数中自带判断是不是首次使用RTC 2,使用 RTC_Get();读出时间。读出的数据存放在: 年 ryear (16位) 月 rmon (以下都是8位) 日 rday 时 rhour 分 rmin 秒 rsec 周 rweek 3,使用 RTC_Set(4位年,2位月,2位日,2位时,2位分,2位秒); 写入时间。例如:RTC_Get(2017,08,06,21,34,00); 其他函数都是帮助如上3个函数的,不需要调用。 注意要使用RTC_Get和RTC_Set的返回值,为0时表示读写正确。 */ #include "sys.h" #include "rtc.h" //以下2条全局变量--用于RTC时间的读取 u16 ryear; //4位年 u8 rmon,rday,rhour,rmin,rsec,rweek;//2位月日时分秒周 //首次启用RTC的设置 void RTC_First_Config(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);//启用PWR和BKP的时钟(from APB1) PWR_BackupAccessCmd(ENABLE);//后备域解锁 BKP_DeInit();//备份寄存器模块复位 RCC_LSEConfig(RCC_LSE_ON);//外部32.768KHZ晶振开启 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待稳定 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//RTC时钟源配置成LSE(外部低速晶振32.768KHZ) RCC_RTCCLKCmd(ENABLE);//RTC开启 RTC_WaitForSynchro();//开启后需要等待APB1时钟与RTC时钟同步,才能读写寄存器 RTC_WaitForLastTask();//读写寄存器前,要确定上一个操作已经结束 RTC_SetPrescaler(32767);//设置RTC分频器,使RTC时钟为1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) RTC_WaitForLastTask();//等待寄存器写入完成 //当不使用RTC秒中断,可以屏蔽下面2条 // RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断 // RTC_WaitForLastTask();//等待写入完成 } //实时时钟初始化 void RTC_Config(void) { //在BKP的后备寄存器1中,存了一个特殊字符0xA5A5 //第一次上电或后备电源掉电后,该寄存器数据丢失,表明RTC数据丢失,需要重新配置 if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判断寄存数据是否丢失 RTC_First_Config();//重新配置RTC BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5 }else{ //若后备寄存器没有掉电,则无需重新配置RTC //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型 if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){ //这是上电复位 } else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){ //这是外部RST管脚复位 } RCC_ClearFlag();//清除RCC中复位标志 //虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行 //但是每次上电后,还是要使能RTCCLK RCC_RTCCLKCmd(ENABLE);//使能RTCCLK RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步 //当不使用RTC秒中断,可以屏蔽下面2条 // RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断 // RTC_WaitForLastTask();//等待操作完成 } #ifdef RTCClockOutput_Enable RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); BKP_TamperPinCmd(DISABLE); BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock); #endif } //RTC时钟1秒触发中断函数(名称固定不可修改) void RTC_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_SEC) != RESET){ } RTC_ClearITPendingBit(RTC_IT_SEC); RTC_WaitForLastTask(); } //闹钟中断处理(启用时必须调高其优先级) void RTCAlarm_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR) != RESET){ } RTC_ClearITPendingBit(RTC_IT_ALR); RTC_WaitForLastTask(); } //判断是否是闰年函数 //月份 1 2 3 4 5 6 7 8 9 10 11 12 //闰年 31 29 31 30 31 30 31 31 30 31 30 31 //非闰年 31 28 31 30 31 30 31 31 30 31 30 31 //输入:年份 //输出:该年份是不是闰年.1,是.0,不是 u8 Is_Leap_Year(u16 year) { if(year%4==0){ //必须能被4整除 if(year%100==0){ if(year%400==0)return 1;//如果以00结尾,还要能被400整除 else return 0; }else return 1; }else return 0; } //设置时钟 //把输入的时钟转换为秒钟 //以1970年1月1日为基准 //1970~2099年为合法年份 //月份数据表 u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表 const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表 //写入当前时间(1970~2099年有效) u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec) { u16 t; u32 seccount=0; if(syear<2000||syear>2099)return 1;//syear范围1970-2099,此处设置范围为2000-2099 for(t=1970;t<syear;t++){ //把所有年份的秒钟相加 if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数 else seccount+=31536000; //平年的秒钟数 } smon-=1; for(t=0;t<smon;t++){ //把前面月份的秒钟数相加 seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加 if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数 } seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 seccount+=(u32)hour*3600;//小时秒钟数 seccount+=(u32)min*60; //分钟秒钟数 seccount+=sec;//最后的秒钟加上去 RTC_First_Config(); //重新初始化时钟 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5 RTC_SetCounter(seccount);//把换算好的计数器值写入 RTC_WaitForLastTask(); //等待写入完成 return 0; //返回值:0,成功;其他:错误代码. } //读出当前时间值 ,返回值:0,成功;其他:错误代码. u8 RTC_Get(void) { static u16 daycnt=0; u32 timecount=0; u32 temp=0; u16 temp1=0; timecount=RTC_GetCounter(); temp=timecount/86400; //得到天数(秒钟数对应的) if(daycnt!=temp){//超过一天了 daycnt=temp; temp1=1970; //从1970年开始 while(temp>=365){ if(Is_Leap_Year(temp1)){//是闰年 if(temp>=366)temp-=366;//闰年的秒钟数 else {temp1++;break;} } else temp-=365; //平年 temp1++; } ryear=temp1;//得到年份 temp1=0; while(temp>=28){//超过了一个月 if(Is_Leap_Year(ryear)&&temp1==1){//当年是不是闰年/2月份 if(temp>=29)temp-=29;//闰年的秒钟数 else break; }else{ if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年 else break; } temp1++; } rmon=temp1+1;//得到月份 rday=temp+1; //得到日期 } temp=timecount%86400; //得到秒钟数 rhour=temp/3600; //小时 rmin=(temp%3600)/60; //分钟 rsec=(temp%3600)%60; //秒钟 rweek=RTC_Get_Week(ryear,rmon,rday);//获取星期 return 0; } //按年月日计算星期(只允许1901-2099年)//已由RTC_Get调用 u8 RTC_Get_Week(u16 year,u8 month,u8 day) { u16 temp2; u8 yearH,yearL; yearH=year/100; yearL=year%100; // 如果为21世纪,年份数加100 if (yearH>19)yearL+=100; // 所过闰年数只算1900年之后的 temp2=yearL+yearL/4; temp2=temp2%7; temp2=temp2+day+table_week[month-1]; if (yearL%4==0&&month<3)temp2--; return(temp2%7); //返回星期值 }
#ifndef __RTC_H #define __RTC_H #include "sys.h" //全局变量的声明,在rtc.c文件中定义 //以下2条是使用extern语句声明全局变量 //注意:这里不能给变量赋值 extern u16 ryear; extern u8 rmon,rday,rhour,rmin,rsec,rweek; void RTC_First_Config(void);//首次启用RTC的设置 void RTC_Config(void);//实时时钟初始化 u8 Is_Leap_Year(u16 year);//判断是否是闰年函数 u8 RTC_Get(void);//读出当前时间值 u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//写入当前时间 u8 RTC_Get_Week(u16 year,u8 month,u8 day);//按年月日计算星期 #endif
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。