以前在单片机学习中,驱动数码管是直接将数码管连接单片机,在单片机上需要编写很复杂的程序,并且对数码管需要实时扫描。在实际项目开发中,一般使用数码管专用控制芯片来驱动数码管,提高开发效率。

TM1640是一种LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU数字接口、数据锁存器、LED驱动等电路。主要应用于电子产品LED显示屏驱动。采用SOP28封装形式。TM1640只是数码管驱动芯片的一种,还有很多其他的驱动芯片,具体芯片选择需要根据项目需求及预算来决定。


可驱动16位数码管。



有共阴极与共阳极两种电路图,根据开发板上的数码管实际连接来决定使用哪个。


这两个引脚分别与单片机的两个GPIO相连。


8位数码管

这八个共阴极的LED灯相当于一个数码管,可以当成一个数码管来处理。

驱动代码如下:

main函数

#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); //延时}}
}

TM1640.c函数

#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();
}

TM1640.h文件

#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

rtc.c函数

/* //时间读写与设置说明//
1,在mani函数开头放入RTC_Config();就可以使能时钟了。
在RTC_Config();函数中自带判断是不是首次使用RTC
2,使用 RTC_Get();读出时间。读出的数据存放在:
年 ryear (16位)
月 rmon   (以下都是8位)
日 rday
时 rhour
分 rmin
秒 rsec
周 rweek3,使用 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模块不需要重新配置,且掉电后依靠后备电池依然运行//但是每次上电后,还是要使能RTCCLKRCC_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);//配置完成后,向后备寄存器中写特殊字符0xA5A5RTC_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); //返回星期值
}

rtc.h文件

#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

STM32F1开发指南笔记20----数码管驱动芯片TM1640解析相关推荐

  1. STM32F1开发指南笔记38----游戏手柄实验

    相信80后小时候都玩过FC游戏机(又称:红白机/小霸王游戏机),那是一代经典,给童年带来了无限乐趣.本章,介绍如何通过STM32来驱动FC游戏机手柄,将FC游戏机的手柄作为战舰STM32开发板的输入设 ...

  2. STM32F1开发指南笔记6----SYSTEM文件夹__delay函数、sys函数、usart函数

    此文件夹是STM32F10x系列的底层核心驱动函数,可以用在STM32F10x系列的各个型号上面,方便快速构建自己的工程.SYSTEM文件夹下包含看delay.sys.usart等三个文件夹.分别包含 ...

  3. [微信开发] 开发指南笔记

    (1)测试号申请 https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login (2)接入指南 1.每个用户对每个公众号有一个唯一的Op ...

  4. Node.js 开发指南笔记

    第一章:node简介 介绍了node是什么:node.js是一个让javascript运行在服务器端的开发平台, node能做些什么:[书上的] 具有复杂逻辑的网站 基于社交网络的大规模Web应用 W ...

  5. 【TDA2x学习】番外篇三、VisionSDK开发指南笔记

    1.介绍 Vision Software Development Kit (SDK)是一种多处理器.多通道的用于TI家族ADAS SoCs的软件开发平台.该软件框架允许用户创建不同的ADAS应用程序数 ...

  6. HTML5移动web开发指南笔记

    手机内置浏览器: Android:Andriod Browser IOS:Mobile Safari BlackBerry:Webkit Symbian S60: Web Browser for S6 ...

  7. 开发指南篇 5:Vue API 盲点解析

    鄙人已付费购买,粘贴出来给更多前端爱好者学习.若掘金小册禁止此类操作,请联系 在了解了一些实用的开发技巧和编码理念后,我们在项目的开发过程中难免也会遇到因为不熟悉 Vue API 而导致的技术问题,而 ...

  8. STM32开发指南阅读记录

    一.STM32的开发方式:标准库,HAL库,寄存器开发 1.寄存器开发:通过直接操作寄存器进行开发,但是由于STM32的寄存器数量众多,逐个查询比较繁琐. 2.标准库:ST公司为每一款芯片都编写了一份 ...

  9. 【SQL Server】数据库开发指南(三)面向数据分析的 T-SQL 编程技巧与实践

    本系列博文还在更新中,收录在专栏:#MS-SQL Server 专栏中. 本系列文章列表如下: [SQL Server] Linux 运维下对 SQL Server 进行安装.升级.回滚.卸载操作 [ ...

  10. 【SQL Server】数据库开发指南(五)T-SQL 高级查询综合应用与实战

    本系列博文还在更新中,收录在专栏:#MS-SQL Server 专栏中. 本系列文章列表如下: [SQL Server] Linux 运维下对 SQL Server 进行安装.升级.回滚.卸载操作 [ ...

最新文章

  1. 特斯拉AI日放大招!发布自研超算Dojo芯片,每秒可执行1024亿次计算
  2. 为什么电脑下载python很慢-Python运行速度慢你知道这是为什么吗?
  3. 《Stellaris》游戏分析报告
  4. NFS精简版配置方法
  5. sql 日期加1天_SQL基础知识——BETWEEN
  6. Ubuntu下使用Git_2
  7. .NET平台常用的框架整理
  8. 苹果mac3D模型渲染软件:KeyShot
  9. 青少年计算机知识,学习计算机程序设计和算法知识对青少年教育有什么帮助?...
  10. c语言词法分析程序实验报告,词法分析器实验报告
  11. JavaWeb和WebGIS学习笔记(七)——MapGuide Open Source安装、配置以及MapGuide Maestro发布地图——超详细!目前最保姆级的MapGuide上手教程!
  12. 拾色器 插件 html,Unity3D 插件开发教程 #3:制作拾色器 ColorPicker
  13. 未找到beta版怎么解决_你要的直播解决方案来了!无需采集卡!
  14. php 遍历文件夹并压成zip_将文件夹压缩成zip文件的php代码
  15. 情人节——圣瓦伦丁节(St. Valentine's Day)
  16. UCLA教授朱松纯:走向通用人工智能,从大数据到大任务
  17. 【云原生】Kubernetes(k8s)Calico 客户端工具 calicoctl
  18. java常用类库——util包
  19. 进入android刷机模式,手机进入recovery模式怎么刷机?【详细步骤】
  20. 57步进电机驱动板,可以通过编码器调速,支持SPI通讯屏显示,485通讯

热门文章

  1. 常用邮箱SMTP服务器地址大全
  2. 利用QGIS免费批量获取Google、Bing等高分辨率卫星影像
  3. 计算机网络第四章课后答案(第七版谢希仁著)
  4. 计算机更换桌面图片,桌面图片高清怎么换?桌面图片怎么美化?
  5. C#编程之SqlHelper
  6. 教师专用录微课及剪辑软件推荐-camtasia及使用教程
  7. C语言游戏开发闪屏解决办法--双缓冲技术
  8. Kindle wifi 连接不上的问题
  9. 【matlab】iir滤波器
  10. Paxos算法和Raft算法