在嵌入式中,为单片机增加扩展芯片是非常普遍的,比如引脚数不够,存储容量不够等等,这个时候就可以考虑加上扩展芯片,来扩展功能。
对于这些芯片,我持的观点是必须要知道有这些芯片,能够做到设计时可以想起来;另外就是确实需要用时,能够快速找到资料,搞懂原理,据此设计电路和程序。为了方便读者寻找,大部分芯片都已上传数据手册,且积分只要0分。
74HC165为一个8位移位寄存器,并行输入,互补串行输出。这是数据手册链接,内部含中英文两个文件。
74HC引脚如下所示:
内部逻辑原理图:
引脚定义:
74HC595芯片是一个串转并的芯片,8位并行输出,还有1位串行输出。我觉得这个了解引脚分布及内部逻辑原理即可。如下图所示。
其中,一般来说,/SCLR接高,使其清零功能无效;/G(有些芯片上叫/OE)接低,使得输出恒有效;因此,关键是控制SER(串行数据输入端)、SCK(第一层输出时序,下降沿有效)、RCK(第二层输出时序,下降沿有效) 三个信号。另外九个为输出端。
另外,需要注意的是QH‘引脚,这个引脚仔细看会发现它和QH的值是一样的,看数据手册会知道,这个引脚是串行数据输出(QA~QH是并行数据输出),一般用来级联多片595芯片,那么问题来了,为什么不直接用QH呢?原因就是QH’前面的两个非门,看似逻辑上没有作用,但其实经过两个非门之后,这个信号的电流增大了,也就是我们常说的电压跟随器,这是考虑到扇出系数的一个设计。
最后,以一个基于595的8位数码管模块的使用例程来加深对595的认识。如下图:
由于这个数码管模块是我偶然得到的,所以我也没有数据手册,于是向万能的淘宝客服求助,最后在一个类似的商品详情界面得到一张原理图【不一定正确,而且经过试验,确实不正确】,如下所示:
先来分析一下:两个595芯片靠QH‘级联,其中数码管的位选(到底哪个数码管亮)是前一个595的并行输出,数码管的段选(数码管的会形成什么数字)是后一个595的并行输出,然后两个595的两个时钟信号RCK和SCK并联,同时控制。
显然,位选和段选应该同时输出才对,但如果单独控制一个595显然不行,但好在595并行输出要经过 “两道坎”(RCK,SCK),因此我们可以先让两个信号依次经过第一道坎,然后一起经过第二道坎,就能够成功显示,51程序如下:
#include "reg52.h"
typedef unsigned int uint;
typedef unsigned char uchar;
sbit DIO = P3^2; //三个信号对应的引脚
sbit SCK = P3^3;
sbit RCK = P3^4;
uchar digital_N[16] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //0-F的【共阴】数码管代码
uchar digital_P[16] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};//0-F的【共阳】数码管
uchar selected[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; //每次选中一个数码管
/****延时1us函数****/
void delay_1us(unsigned int i)
{
while(i--);
}
/****在第number个数码管显示数字dat***/
void display(uchar number, uchar dat)
{
uchar j;
RCK = 1;
SCK = 1;
for(j=0; j<8; j++) //先把后面那个595需要并行输出的输出到前面595中转
{
DIO = number >> 7; //右移7位,先取最高位
number <<= 1; //左移一位,次高位变最高位
SCK = 0;
delay_1us(1);
SCK = 1;
}
for(j=0; j<8; j++) //再把前面那个595需要输出的数锁存,同时之前中转的量也会移到后面的595中锁存
{
DIO = dat >> 7;
dat <<= 1;
SCK = 0;
delay_1us(1);
SCK = 1;
}
RCK = 0; //一起输出锁存的数字
delay_1us(1); //这里不需要延时很长,不然数码管会闪烁
RCK = 1;
}
void main()
{
while(1)
{
//显示“20220223”
display(selected[0], digital_P[2]);
display(selected[1], digital_P[0]);
display(selected[2], digital_P[2]);
display(selected[3], digital_P[2]);
display(selected[4], digital_P[0]);
display(selected[5], digital_P[1]);
display(selected[6], digital_P[2]);
display(selected[7], digital_P[3]);
}
}
MAX7219/MAX7221其实功能和74HC595很类似,都有串行输入,串行输出,并行输出等功能,因此也可以实现级联,但是显然MAX7219的功能要比595强大得多。
管脚 | 名称 | 功能 |
---|---|---|
1 | DIN | 串行数据输入端口。在时钟上升沿时 数据被载入内部 的 16 位寄存器 |
2,3,5-8,10,11 | DIG 0–DIG 7 | 对应寄存器选中时输出低电平,掉电模式 下7219输出高电平,7221为高阻 |
4,9 | GND | 地线(4 脚和 9 脚必须同时接地) |
12 | LOAD (MAX7219)/ CS(MAX7221) |
载入数据。上升沿时将16位数据锁存到内部寄存器中。 (7221)CS端传输数据时需设定为低电平 |
13 | CLK | 时钟序列输入端。在时钟的上升沿, 数据移入内部 移位寄存器。下降沿时,数据从 DOUT端输出。 |
14-17,20-23 | SEG A–SEG G, DP | 默认输出为高电平,掉电模式下输出为低电平。 |
18 | SET | 通过一个电阻连接到 VDD来提高段电流。 最小电阻不小于9.53k |
19 | V+ | 正极电压输入,+5V |
24 | DOUT | 串行数据输出端口,从 DIN 输入的数据在16.5个时钟周期后 在此端有效。当使用多个 MAX7219时用此端方便扩展。 |
需要注意的是:MAX7219和MAX7221二者功能和引脚差不多,但也有一些差别,根据数据手册,MAX7219为LOAD引脚,MAX7221为CS引脚;此外,MAX7221支持SPI,QSPI,MICROWIRE等串行协议。
3. 内部结构图
MAX7219内部结构图如下所示:
其实这张图我有点不想放出来,因为这张图误导性很强! 因为会让人觉得D0-D7只能到SEG输出端口中,但实际上还可以用来设置寄存器,但是考虑到完整性,还是把这张图放上来。注意不要纠结于这一张图。
4. 工作时序
在MAX7219中具有14个可独立寻址的寄存器,而这也是编程的重点。其地址表如下所示。
在使用时,就是不断向MAX7219写入控制字的过程,其控制字的格式如下图所示:
由图可知,每一条控制指令都是16位的,其中,高四位不用管,D8~D11为寄存器的地址(上图最后一列),低8位为需要送到寄存器中的数据。
那每一条指令如何传输呢?一般来说,控制MAX7219时只需要控制三个引脚即可:LOAD/CS、DIN、CLK。其时序图如下所示:
从图中我们可以看出,在数据传输时,LOAD/CS引脚为低电平,而DIN引脚的数据在上升沿时被送入内部的16位移位寄存器。
注意:根据数据手册,MAX7219的LOAD端在数据传输时无论处于何种状态,都不影响数据在时钟上升沿时传输;但MAX7221的CS端在数据传输时必须为低电平。
此外,不管是7219还是7221,在16位数据传输完毕后,即第16个上升沿之后、第17个上升沿之前LOAD/CS引脚必须变为高电平,否则数据会丢失。
5. 寄存器使用详解
Decode Mode:用来设置输出是否带译码
前文提到:MAX7219非常适合驱动数码管的一个原因就在这,因为这个寄存器就可以设置输出是否带译码,实现输出0x02就能在数码管上显示2,此时就需要选择对D0-D3进行译码,其对应关系如下表所示(另外两种译码方式不常用)
Intensity:控制显示亮度(电流强度)寄存器
首先需要明确,一般在使用MAX7219时,在VDD与引脚ISET之间必须连接一个电阻,一般不小于9.53k,如下图所示
这个电阻决定了数码管的最大的电流,但是MAX7219可以控制电流为最大电流的几分之几,其寄存器数值及含义如下图所示:
Scan Limit:扫描控制寄存器
扫描控制寄存器用来设定扫描显示器的个数,从 1 个到 8 个.它们将以 800Hz 的扫描速率进行多路扫描显示。如果数据少的话,扫描速率为 8*fosc/N,N 是指需要扫描数字的个数。扫描数据的个数影响显示亮度,所以不能将扫描寄存器设置为空扫描。
注意:扫描数不能设置太少(< 3)否则个别驱动电流会增大。
Display Test:显示检测寄存器
显示检测寄存器有正常和显示检测两种工作状态。显示检测状态在不改变所有其他控制和数据寄存器(包括关闭寄存器)的情况下将所有 LED 都点亮。在此状态下,8 个数据都会被扫描,工作周期为 31/32。
DIG0-DIG7【重点!】
以上的寄存器属于控制寄存器,一般来说在初始化时就设定好了,之后想要显示什么内容还是得要控制DIG0~DIG7这8个数据寄存器。
为了能够更好地理解这8个数据寄存器,我们不妨代入到8位数码管控制中,如引脚名字所显示,DIG0~DIG7为位选,当选中某一个数码管时(即控制指令的地址为8个其中之一)则该引脚输出低电平(低电平片选),而跟随控制指令传输过去的数据即为数码管的段选SEG A ~ SEG G,决定了数码管显示什么内容。
No-Op:不工作寄存器
当有多个MAX7219或MAX7221被串接使用时要用到不工作寄存器。把所有的芯片的LOAD/CS 端联接在一起,把相邻的芯片的DOUT和DIN连接在一起。DOUT是一个CMOS逻辑电平的输出口,它可以很容易的驱动下一级的DIN口。
例如,如果四个MAX7219被连接起来使用,然后向第四个芯片发送必要的16位数据,后面跟三组NO-OP代码(十六进制的0xX0XX)。然后使LOAD/CS 端变为高电平,数据则被载入所有芯片。前三个芯片接收到 NO-OP 代码,第四个接收到有效数据。
和595一样,这里以一个16*8的点阵屏例程来加深对MAX7219的认识。如下图所示:
其原理图如下所示,这个是8x8的,16x8的就是在此基础上级联了一块。
由于DIG0~DIG7在“选中”时输出低电平,因此这8个引脚连接点阵屏的阴极,而SEG系列引脚就连点阵屏的阳极。所以在显示时只能“一行一行”地显示。
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
sbit CS = P2^1;
sbit CLK = P2^2;
sbit DIN = P2^0; //三个信号引脚
void Transmit(unsigned long Data, unsigned char Len) //一次性传输Len长度的数据Data
{
signed char i;
CS = 1; CLK = 0; //初始化
CS = 0; //片选,开始传输
for(i = Len; i > 0; i--)
{
CLK = 0
DIN = Data & 0x01; //根据系统内部图,先传低位
Data >>= 1; //右移一位
CLK = 1; //上升沿锁存数据
}
CS = 1; //LOAD拉高,实现数据放入内部寄存器
}
有了这个函数之后,接下来就是把一些定义好的指令作为这个函数的参数传输到点阵屏模块了。
PCA9685:I2C转16路PWM,助力你的系统 - 简书
PCA9685 16路12位pwm信号发生器 - 个人网站
PCA9685 多舵机控制器的编程 - CSDN
Arduino uno使用PCA9685模块实现16路舵机控制 - CSDN
PCA9685是一个支持IIC协议的16路PWM输出控制器,即只需要IIC的两个信号引脚,就能控制16路PWM的输出,且能单独输出。其特性概况如下:
其引脚图如下所示:
其定义如下:
其中,A0~A5用来确定器件的IIC地址,如下图所示:
因此,在使用时,最多有2^6=64个地址,但是其中有两个地址被保留,分别是LED All Call address (E0h,1110 000)以及Software Reset address(06h,0000 0110),其中LED All Call地址为通用地址,可以给所有从机下达指令。Software Reset address地址用来由主机重置所有从机。
LED0~LED15为16路PWM输出,可单独控制。
OE为使能引脚,这个引脚低电平使能,不接的话模块内部默认已经接地使能了,所以正常使用可以不接。
EXTCLK为外部时钟信号输入端,如果不使用外部时钟,这个引脚得接地。
SCL和SDA为IIC的两个信号引脚,为芯片的控制信号。
PCA9685内部有非常多的寄存器,但好在其分布较为有规律:
内部地址(hex) | 名称 | 功能 |
---|---|---|
00 | MODE1 | 设置寄存器1 |
01 | MODE2 | 设置寄存器2 |
02 | SUBADR1 | i2c-bus subaddress1 |
03 | SUBADR2 | i2c-bus subaddress2 |
04 | SUBADR3 | i2c-bus subaddress3 |
05 | ALLCALLADR | |
06 | LED0_ON_L | |
07 | LED0_ON_H | |
08 | LED0_OFF_L | |
09 | LED0_OFF_H | |
… | … | … |
0x06 + 4*X | LEDX_ON_L | |
0x06 + 4*X + 1 | LEDX_ON_H | |
0x06 + 4*X + 2 | LEDX_OFF_L | |
0x06 + 4*X + 3 | LEDX_OFF_H | |
… | … | … 上面共16路通道 |
FA | ALL_LED_ON_L | |
FB | ALL_LED_ON_H | |
FC | ALL_LED_OFF_L | |
FD | ALL_LED_OFF_H | |
FE | PRE_SCALE | 控制周期的寄存器 |
FF | TestMode |
总结来说,主要使用的寄存器为:
下面分别讲述:
位 | 名称 | 功能 |
---|---|---|
D7 | RESTART | 写1复位,写完后此位自动清除。一定要在SLEEP位写0后至少500us后才能对此位写1进行复位。 |
D6 | EXTCLOCK | 0-使用内部时钟(25MHz)。1-使用外部时钟引脚的时钟。修改此位前,一定要先SLEEP,再修改此位(此时SLEEP位仍然写1),再退出SLEEP。 |
D5 | AI | 0-内部地址读写后不自动增加。1-内部地址读写后自动增加。一般i2c设备在对从机读写后内部地址都会自动增加,这个芯片可以手动设置是否自动增加,我们一般都会设成自动增加。 |
D4 | SLEEP | 0-退出SLEEP模式。1-进入SLEEP模式。注:1、写0退出sleep模式后,最多等500us后即可产生稳定的时钟信号。2、写1进入sleep模式后,时钟会关闭。此时可以修改时钟源寄存器EXTCLOCK和周期寄存器PRE_SCALE,修改这两个寄存器之前必须先进入sleep模式。 |
D3 | SUB1 | |
D2 | SUB2 | |
D1 | SUB3 | |
D0 | ALLCALL | 0-不响应0x70通用i2c地址。1-响应0x70通用i2c地址。这个芯片除了可以通过A5:A0自定义i2c地址外,还有一个通用i2c地址0x70,此寄存器可以控制是否响应这个通用地址。注意啊:这个寄存器的设置好像掉电会保存的! |
注意:实际应用中发现有误差,需要加入校准,要把udpate_rate乘以0.915。【根据实际情况选择】
其内部的工作原理是:系统中有一个12位的计数ACK,ACK根据PRE_SCALE寄存器设置的周期进行增加,没增加一次就会和上述四个寄存器对比:
当发现 ACK == LEDX_ON_H[3:0]:LEDX_ON_L 时,X通道输出高电平;
当发现 ACK ==LEDX_OFF_H[3:0]:LEDX_OFF_L 时,X通道输出低电平。
因此,在使用时,先确定需要产生的PWM周期,如果是控制舵机则一般为50Hz,即周期为20ms,然后将周期分为4095份,LEDn_ON为上升沿出现时间,LEDn_OFF为下降沿出现时间,因此占空比为二者的差值。
【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)- CSDN
24C02是一个支持IIC协议的EEPROM芯片,“02”代表其容量为2k位。还有其他容量的芯片,如下图所示:
其芯片引脚图如下图所示:
引脚定义:
引脚名称 | 说明 |
---|---|
A0-A2 | 地址输入线(即IIC地址) |
SDA | 数据线 |
SCL | 时钟线 |
WP | 写保护,WP为低电平时,正常写入数据 当WP为高电平时,写保护,只读模式。 |
GND、VCC | 提供电源 |
由于24C02的通信协议为IIC,因此这个器件必然存在访问地址,如下图所示:
在了解存储读写方式前,需要了解24C02的存储结构
其中,需要注意的是页和字地址,所谓页,可以理解为中间的一个存储单元,可以实现一次性连续读写(后面会有介绍),而字地址就是从00H~FFH逐次加1。
写存储有两种方式:字节写和页写。顾名思义,就是一次性写入一个字节还是一页。
当前地址读
内部地址计数器保存着上次访问时最后一个地址加1的值。只要芯片有电,该地址就一直保存。当读到最后页的最后字节,地址会回转到0;当写到某页尾的最后一个字节,地址会回转到该页的首字节。
注意:在传输数据结束之后,不需要应答位
顺序读
顺序读可以通过“当前地址读”或“随机读”启动。主器件接收到一个数据后,应答ACK。只要24C02接收到ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器地址末,地址自动回转到0,仍可继续顺序读取数据。
今天学习步进电机的控制方法,认识了一个芯片——26LS31,觉得很有意思,记录一下使用方法。
首先,还是先去找一下数据手册,即datasheet,看看这个。
这个芯片其实只是一般的TTL逻辑控制芯片,但它因为在步进电机驱动器中使用较多,因此“有点名气”。其特性如下图所示。
对于一个数字芯片,最为主要的就是了解它的运行逻辑了,因为这直接关系到能否使用这个芯片。从数据手册上扒下来一张逻辑原理图。
一个数字芯片,如果只掌握了其运行逻辑,很有可能会在使用时遇到一些问题且无从下手,因此,学习读数据手册,不要忽略电气特性部分,就显得很有必要了。
本人还没开始使用,工程经验欠缺,建议参考上面的那篇文章。