HT16C21 是一款功能不错的LCD驱动控制器,通过I2C接口与微控制器通信,能够驱动最多20×4或16×8点的LCD显示。由于DSP28335可能没有硬件I2C资源,或者硬件I2C配置不够灵活,软件模拟I2C(Software I2C) 就成了一个实用的选择。
HT16C21 驱动模拟I2C实现
软件I2C关键实现要点
软件模拟I2C的核心在于通过GPIO引脚精确模拟I2C协议的时序,包括起始条件、停止条件、数据读写和应答信号。
- 引脚定义与初始化:需要定义两个GPIO引脚分别作为SDA(数据线)和SCL(时钟线),并将它们初始化为输出模式,初始状态通常设置为高电平。
- 时序模拟 :严格按照I2C协议的时序要求,通过控制GPIO引脚的高低电平时序来产生起始信号、停止信号、数据有效等。需要注意的是,I2C协议规定SCL为高电平时,SDA的数据必须稳定。
- 数据收发:逐位读写数据,每个时钟脉冲传输一位数据。写数据时,先传输最高位(MSB);读数据时,同样先读取最高位。
- 应答处理:每传输完一个字节(8位)后,接收方(无论是主机还是从机)需要发送一个应答信号(ACK)或非应答信号(NACK)。
DSP28335 模拟I2C驱动HT16C21代码
1. 宏定义与引脚控制
首先定义SDA和SCL引脚,以及基本的引脚操作宏:
c
// 假设使用GPIO31作为SDA, GPIO32作为SCL
#define I2C_SDA_GPIOPIN 31
#define I2C_SCL_GPIOPIN 32
// 控制SDA和SCL引脚输出高电平
#define I2C_SDA_HIGH GpioDataRegs.GPASET.bit.GPIO31 = 1
#define I2C_SCL_HIGH GpioDataRegs.GPASET.bit.GPIO32 = 1
// 控制SDA和SCL引脚输出低电平
#define I2C_SDA_LOW GpioDataRegs.GPACLEAR.bit.GPIO31 = 1
#define I2C_SCL_LOW GpioDataRegs.GPACLEAR.bit.GPIO32 = 1
// 设置SDA引脚为输入方向(用于读取)或输出方向(用于写入)
#define I2C_SDA_INPUT GpioCtrlRegs.GPADIR.bit.GPIO31 = 0
#define I2C_SDA_OUTPUT GpioCtrlRegs.GPADIR.bit.GPIO31 = 1
// 读取SDA引脚的电平状态
#define I2C_READ_SDA GpioDataRegs.GPADAT.bit.GPIO31
// 简单的延时函数,需要根据CPU时钟频率调整
void I2C_Delay(void)
{
Uint16 i;
for (i = 0; i < 100; i++);
}
2. I2C基本信号模拟
模拟I2C的起始信号、停止信号、应答信号等:
c
// I2C起始信号:SCL高电平期间,SDA从高变低
void I2C_Start(void)
{
I2C_SDA_OUTPUT; // 设置SDA为输出
I2C_SDA_HIGH;
I2C_SCL_HIGH;
I2C_Delay();
I2C_SDA_LOW;
I2C_Delay();
I2C_SCL_LOW; // 保持SCL低电平,准备发送数据
I2C_Delay();
}
// I2C停止信号:SCL高电平期间,SDA从低变高
void I2C_Stop(void)
{
I2C_SDA_OUTPUT; // 设置SDA为输出
I2C_SDA_LOW;
I2C_Delay();
I2C_SCL_HIGH;
I2C_Delay();
I2C_SDA_HIGH;
I2C_Delay();
}
// 发送应答信号(ACK):SCL低电平期间,SDA拉低;SCL产生一个脉冲
void I2C_SendAck(void)
{
I2C_SDA_OUTPUT;
I2C_SDA_LOW;
I2C_Delay();
I2C_SCL_HIGH;
I2C_Delay();
I2C_SCL_LOW;
I2C_Delay();
}
// 发送非应答信号(NACK):SCL低电平期间,SDA拉高;SCL产生一个脉冲
void I2C_SendNack(void)
{
I2C_SDA_OUTPUT;
I2C_SDA_HIGH;
I2C_Delay();
I2C_SCL_HIGH;
I2C_Delay();
I2C_SCL_LOW;
I2C_Delay();
}
// 检查从机应答信号
// 返回值:0-应答成功,1-无应答
Uint16 I2C_CheckAck(void)
{
Uint16 ack;
I2C_SDA_INPUT; // 设置SDA为输入,准备读取从机应答
I2C_Delay();
I2C_SCL_HIGH; // SCL拉高,在此期间读取SDA状态
I2C_Delay();
if (I2C_READ_SDA) {
ack = 1; // SDA为高,表示无应答(NACK)
} else {
ack = 0; // SDA为低,表示应答(ACK)
}
I2C_SCL_LOW;
I2C_Delay();
I2C_SDA_OUTPUT; // 将SDA重新设置为输出,以便后续操作
return ack;
}
3. I2C字节读写函数
实现通过I2C读写一个字节的数据:
c
// 通过I2C写入一个字节数据
void I2C_WriteByte(Uint8 data)
{
Uint8 i;
I2C_SDA_OUTPUT;
for (i = 0; i < 8; i++) {
if (data & 0x80) { // 先传输最高位(MSB)
I2C_SDA_HIGH;
} else {
I2C_SDA_LOW;
}
I2C_Delay();
I2C_SCL_HIGH; // SCL上升沿,数据有效
I2C_Delay();
I2C_SCL_LOW;
I2C_Delay();
data <<= 1; // 左移,准备发送下一位
}
// 发送完8位数据后,读取从机应答
I2C_CheckAck();
}
// 通过I2C读取一个字节数据
Uint8 I2C_ReadByte(void)
{
Uint8 i, data = 0;
I2C_SDA_INPUT; // 设置SDA为输入,准备读取数据
for (i = 0; i < 8; i++) {
data <<= 1; // 左移,准备接收下一位
I2C_Delay();
I2C_SCL_HIGH; // SCL拉高,在此期间读取SDA状态
I2C_Delay();
if (I2C_READ_SDA) {
data |= 0x01; // SDA为高,该位为1
}
I2C_SCL_LOW;
}
I2C_SDA_OUTPUT; // 将SDA重新设置为输出,以便后续发送应答
return data;
}
4. HT16C21初始化与显示控制
HT16C21需要正确的初始化才能正常工作,以下是一个初始化示例:
c
// HT16C21初始化
void HT16C21_Init(void)
{
// 发送起始信号
I2C_Start();
// 发送HT16C21的I2C地址(7位地址为0x70,左移一位后写操作为0xE0)
I2C_WriteByte(0x70 << 1); // 假设最后一位为0,表示写操作
// 发送配置命令
// 设置系统振荡器开、显示开
I2C_WriteByte(0x84); // 系统振荡器开、显示开 的命令字节
// 具体配置值需参考数据手册,例如0x84可能表示:1000 0100,即开启振荡器和显示
// 设置显示参数,如占空比(Duty)、偏置(Bias)
I2C_WriteByte(0x82); // 假设0x82是设置Duty和Bias的命令
I2C_WriteByte(0x00); // 具体的配置数据,例如1/4 Duty, 1/3 Bias
// 设置帧频率
I2C_WriteByte(0x86);
I2C_WriteByte(0x01); // 例如设置帧频为160Hz
// 关闭闪烁
I2C_WriteByte(0x88);
I2C_WriteByte(0x00); // 关闭闪烁
// VLCD电压调整设置(如果需要)
I2C_WriteByte(0x8A);
I2C_WriteByte(0x10); // 具体的电压调整值,需参考数据手册
// 发送停止信号
I2C_Stop();
}
5. 向HT16C21写入显示数据
初始化完成后,可以向其显示RAM写入数据:
c
// 向HT16C21的指定地址写入显示数据
void HT16C21_WriteData(Uint8 address, Uint8 data)
{
// 发送起始信号
I2C_Start();
// 发送HT16C21的I2C地址(写操作)
I2C_WriteByte(0x70 << 1); // 7位器件地址为0x70,左移一位,最低位为0表示写
// 发送写显示RAM的命令(0x80)和地址
// HT16C21的显示RAM读写命令通常为0x80,与地址组合
I2C_WriteByte(0x80 | address); // 组合命令和地址
// 写入显示数据
I2C_WriteByte(data);
// 发送停止信号
I2C_Stop();
}
使用示例
在主函数中,你可以这样使用:
c
void main(void)
{
// 初始化系统控制、GPIO等
InitSysCtrl();
InitGpio(); // 确保初始化了用于I2C的GPIO引脚
// 初始化软件I2C
// (GPIO方向设置等已在宏定义中体现,可根据需要额外初始化)
// 初始化HT16C21
HT16C21_Init();
// 向HT16C21的显示RAM地址0x00写入数据0xAB
HT16C21_WriteData(0x00, 0xAB);
while(1)
{
// 主循环,可以不断更新显示内容
}
}
参考代码 HTC16C21 驱动模拟I2C www.youwenfan.com/contentcsh/56960.html
重要参考:HT16C21常用命令字节
根据HT16C21的数据手册,以下是一些常用的命令字节概要:
命令字节 (Command Byte) | 功能描述 | 典型数据字节 (Data Byte) | 说明 |
---|---|---|---|
0x82 | 设置Duty和Bias | 0x00 | 例如:1/4 Duty, 1/3 Bias |
0x84 | 系统振荡器与显示开关控制 | 0x03 | 开启系统振荡器和显示 |
0x86 | 设置帧频率 (Frame Frequency) | 0x01 | 例如:160Hz |
0x88 | 设置闪烁模式 (Blinking) | 0x00 | 关闭闪烁 |
0x8A | VLCD电压调整 (Voltage Adjust) | 0x10 | 具体的电压调整值,需参考数据手册 |
0x80 | 显示RAM读写命令 (结合地址使用) | - | 与地址组合,如`0x80 |
希望以上代码和说明能帮助你在DSP28335上成功驱动HT16C21。软件模拟I2C虽然会占用一些CPU时间,但提供了很好的灵活性。调试时最好用示波器或逻辑分析仪观察I2C时序,这能帮你快速定位问题。