单片机怎么控制带74hc595驱动的4位数码管的亮度
某论坛上看到一个人提问，把我的回答也记在这边做个记录。原问题现在是4位数码管，共阳，已经驱动74hc595让4位数码管亮起来了，现在在这基础上再控制数码管上数字的亮度，想过用pwm控制，但是，一般来说pwm管脚不是直连数码管阴极的吗？现在是单片机直连595驱动，595驱动上的管脚连接数码管，从而点亮数码管的，所以不知道该怎么解决？回答都没答到到点子上。用595驱动时一样可以用PWM的概念控制亮度。楼主一定是觉得亮度太高想降低亮度对不对。你可以这样，我自己就是这么做的，完全没有任何问题。首先定义一个最高亮度，比如200，这个数字可以随意定，数字越大可控制的亮度级别越精细#define MAX_BRIGHT 200然后定义一个你需要的亮度，这个亮度取值0-200，比如2#define BRIGHT 2再定义1个变量用于PWM计数uchar nowPwm;然后在main函数的while循环中（或者定时器中）写以下代码 while(1){ nowPwm++; if (nowPwm<=BRIGHT) { // PWM计数还没有超过你指定亮度值的时候点亮数码管（也就是让位选端的IO通电） // 根据你自己的写法填在这里 } else { // 当PWM计数已经超过你设置的亮度值以后，就关闭所有数码管的输出 // 根据你自己的写法填在这里 } // 计数超过最大亮度值时计数值归零，进行下一个PWM的Cycle if (nowPwm>=MAX_BRIGHT) { nowPwm=0; } }
在之前的章节，我们曾经用树莓派的IO口输出不同调宽的PWM信号来控制LED灯的亮度。本文使用芯片TLC5940来输出多路PWM信号以同时控制多达16个LED的亮度。在本文的基础上你可以发挥想象，制作出各种有趣的彩灯效果。最终效果TODO视频演示硬件TLC5940芯片 X 1LED X 32-3k电阻一只原理说明简单的说，TLC5940是一个拥有16路输出的LED驱动芯片，每一路输出支持4096级！亮度调节。老规矩，看一下它的引脚图：跟本文无关的内容或设置在此不做说明，请自己查询datasheet：引脚功能OUT0-1516路PWM信号输出（负极）VPRG指定工作模式，接GND是我们需要的PWM模式SIN串行移位输入SCLK串行移位寄存器时钟（上升沿有效）XLAT从移位寄存器写入GS寄存器（下文介绍）时钟（上升沿有效）BLANK当设为高电平时，关闭所有输出，GS计数器（下文介绍）也将重置IREF跟GND之间连接一个电阻，阻值决定了输出电流的最大值GSCLKGS时钟输入（下文介绍）SOUT串行输出，级联多块芯片时使用先将BLANK设置为H，关闭所有输出。输入各组亮度信息。跟我们之前学过的芯片一样，这款芯片同样是通过移位寄存器来输入数据的。移位寄存器用于传送12bit X 16组PWM数值GSn(n=0-15)，共192bit。每组数据的值范围是0-4095。传送顺序是倒序的：GS15，GS14。。。GS0。先传输的是OUT15的12位数据，其次是OUT14的12位数据。。。最后是OUT0的12位数据。GSn决定了OUTn的PWM调宽。（GSn / 4095 = 0% - 100%）192位数据全部传送完毕后制造XLAT的上升沿，将移位寄存器里的数据写入GS寄存器。BLANK设置为L，打开所有输出准备工作完毕，为了让TLC5940芯片正常工作，还需要向GSCLK输送时钟信号。啥是时钟信号？简单的说就是高低电平不停交替的信号，也称作方波信号。TLC5940会根据这个时钟信号进行从0-4095的计数（高低电平每交替一次数一次），一边计数一边检查各GSn的设定值，一旦到达GSn的值，则切换OUTn的电平一直计数到4095为止，再从0开始重新计数。从0计数到4095就是一个高低电平的切换周期。所以我们给5940提供的时钟频率越快，最后输出的PWM频率就越快。最后输出的PWM的频率 = 时钟信号的频率 / 4095。比如时钟频率是8M，则最后从各OUT口输出的PWM频率是8MHz / 4095 = 1.953KHz。根据TLC5940的官方文档，GSCLK可支持最高30MHz的时钟频率，也就是最高可以输出7.3KHz的PWM信号。如果是控制LED的亮度，那么让人眼感觉不到闪烁的最低频率应该是50Hz以上，所以我们应该至少给GSCLK提供不低于50 X 4095 = 200KHz的方波。树莓派Python的GPIO库翻转IO口的速度不高，实测速度只能达到60KHz，导致最后输出的PWM频率只有14Hz，会有明显的闪烁感。但我们主要是为了说明原理，明白原理了可以用别的方法来实现功能，比如使用c语言的wiringPi库的话效率会高不少，甚至可以使用外部的单片机或其他电路来产生高频率的时钟信号提供给TLC5940，这个作者也在探索中。以后有空再更新。硬件连接模块1引脚模块2引脚TLC5940VPRG树莓派GPIO17TLC5940SIN树莓派GPIO13TLC5940SCLK树莓派GPIO19TLC5940XLAT树莓派GPIO27TLC5940BLANK树莓派GPIO23TLC5940IREF树莓派GND(中间串联一个2-3k的电阻)TLC5940SOUT树莓派GPIO4TLC5940OUT0LED0负极TLC5940OUT1LED1负极TLC5940OUT2LED2负极TLC5940VCC树莓派5VTLC5940GND树莓派GNDLED0,1,2正极树莓派5V代码（Python）#!/usr/bin/env python import RPi.GPIO as GPIO import time VPRG=17 SIN=13 SCLK=19 XLAT=27 BLANK=23 DCPRG=18 GSCLK=4 # 传输GSData(0-4095) def setGSData(data): # print "" # print "S-----------setByte---------------:", hex(data) for bit in range(0,12): # 传入的数字从高位到低位依次判断是否为1，若为1则设置高电平，否则设置低电平 # 判断的方法是先向左移位，把要判断的位移动到最高位然后跟0x800（1000 0000 0000）相与， # 如果结果仍然是0x80（1000 0000 0000）就表示最高位是1，否则最高位就是0 if ((data<<bit) & 0x800 == 0x800): setBitData(True) # print "1", else: setBitData(False) # print "0", # print "" # print "E-----------setByte---------------" def setBitData(data): GPIO.output(SCLK, False) GPIO.output(SIN, data) GPIO.output(SCLK, True) # 输出GSCLK时钟信号 def runGSCLK(): i=0 while(1): i+=1 if i>=4096: # 注意，每次计数到4095时需要手动重置一次芯片的计数器 GPIO.output(BLANK, True) #time.sleep(0.001); GPIO.output(BLANK, False) i=0 else: GPIO.output(GSCLK, True) GPIO.output(GSCLK, False) try: GPIO.setmode(GPIO.BCM) GPIO.setup(VPRG, GPIO.OUT) GPIO.setup(SIN, GPIO.OUT) GPIO.setup(SCLK, GPIO.OUT) GPIO.setup(XLAT, GPIO.OUT) GPIO.setup(BLANK, GPIO.OUT) GPIO.setup(DCPRG, GPIO.OUT) GPIO.setup(GSCLK, GPIO.OUT) # VPRG设置为L，使其工作在GS mode GPIO.output(VPRG, False) # BLANK设置为H，关闭所有输出 GPIO.output(BLANK, True) GPIO.output(DCPRG, True) # 传送12bit X 16组PWM数值GSn(n=0-15)，共192bit # 每组数据的值范围是0-4095 # 因为是通过移位寄存器传输，所以传送顺序是倒序的：GS15，GS14。。。GS0 # GSn决定了OUTn的PWM调宽。（GSn / 4095 = 0% - 100%） setGSData(0) # GS15 本文不使用15-3号输出，设为0 setGSData(0) # GS14 setGSData(0) # GS13 setGSData(0) # GS12 setGSData(0) # GS11 setGSData(0) # GS10 setGSData(0) # GS9 setGSData(0) # GS8 setGSData(0) # GS7 setGSData(0) # GS6 setGSData(0) # GS5 setGSData(0) # GS4 setGSData(0) # GS3 # print "GS2" setGSData(4095) # GS2 # print "GS1" setGSData(2500) # GS1 # print "GS0" setGSData(1000) # GS0 # 送完GS数据后，创造XLAT的上升沿，将移位寄存器的数据一次性送入GS寄存器 GPIO.output(XLAT, False) GPIO.output(XLAT, True) # BLANK设置为L，打开所有输出 # GPIO.output(BLANK, False) # 准备工作完毕，下面向GSCLK输送时钟信号（高低电平交互的方波信号） # TLC5940会根据这个时钟信号进行从0-4095的计数，一边计数一边检查各GSn的设定值，一旦到达GSn的值，则切换OUTn的电平 # 一直计数到4095为止，再从0开始重新计数。从0计数到4095就是一个高低电平的切换周期。 # 所以我们给5940提供的时钟频率越快，最后输出的PWM频率就越快 # 最后输出的PWM的频率 = 时钟信号的频率 / 4095 # 比如时钟频率是8M，则最后从各OUT口输出的PWM频率是8MHz / 4095 = 1.953KHz # 根据TLC5940的官方文档，GSCLK可支持最高30MHz的时钟频率，也就是最高可以输出7.3KHz的PWM信号。 # 如果是控制LED的亮度，那么让人眼感觉不到闪烁的最低频率应该是50Hz以上， # 所以我们应该至少给GSCLK提供不低于50 X 4095 = 200KHz的方波。 # 树莓派Python的GPIO库翻转IO口的速度不高，实测速度只能达到60KHz， # 导致最后输出的PWM频率只有14Hz，会有明显的闪烁感 runGSCLK() except KeyboardInterrupt: pass # 清理GPIO口 GPIO.cleanup()系列文章树莓派GPIO入门01-使用GPIO接口控制发光二极管闪烁树莓派GPIO入门02-GPIO控制LED亮度，制作呼吸灯效果树莓派GPIO入门03-GPIO控制RGB彩色LED灯树莓派GPIO入门04-使用按钮树莓派GPIO入门05-驱动数码管显示数字树莓派GPIO入门06-跟数字湿温度计DHT11通信树莓派GPIO入门07-利用声音传感器制作声控灯树莓派GPIO入门08-使用74HC595芯片驱动数码管（一）树莓派GPIO入门08-使用74HC595芯片驱动数码管（二）树莓派GPIO入门09-使用MAX7219芯片驱动8位数码管树莓派GPIO入门10-使用TLC5940芯片输出多路PWM【当前文章】树莓派GPIO入门11-驱动液晶屏幕（一）树莓派GPIO入门11-驱动液晶屏幕（二）