stm32外设笔记-LCD(二)


本文用到的实验平台:

  • 野火MINI-stm32开发板
  • STM32CUBE-IDE开发工具

1、PCF8574介绍

前面的文章中我介绍了stm32中iic的用法和LCD屏幕1602|12864的使用这两个方面,其中1602和12864都是可以用更少的线的方法来驱动的,但其实我们使用一些外部芯片也许能达到更好的效果呢。

上面提到的两篇文章链接(本文是依赖于这两篇文章的基础而来的):
stm32配置总结-iic的使用
stm32外设笔记-LCD(一)

我们常用的是PCF8574如下所示,只要和LCD1602直接对着插上就可以用了,相关的特殊位置我已经标出

在这里插入图片描述
还是跟以前一样,这里提供数据手册如下:PCF8574中文手册.pdf
在这里插入图片描述

根据数据手册的信息:

  • 模块是有两线双向总线(I2C)扩展为8位输入/输出(I/O)扩展器设计,支持2.5 v- 6 v的电平操作PCF8574通过I2C接口(串行时钟(SCL)、串行数据(SDA)提供通用远程I / O扩展支持大多数微控制器。
  • 设备功能1个8位准双向 I / O端口(P0-P7) (“准双向口”不是真正的双向口)
  • PCF8574 电流消耗很低,且口输出锁存具有大电流驱动能力,可直接驱动 LED
  • 它还带有一条中断接线( INT)可与 MCU 的中断逻辑相连。通过 INT 发送中断信号, 远端 I/O 口不必经过 I2C 总线通信就可通知 MCU 是否有数据从端口输入。 这意味着 PCF8574可以作为一个单被控器

常见的有两种型号,为PCF8574T和PCF8574AT,封装是一眼的歌

在这里插入图片描述

在驱动LCD1602上的示例:液晶显示一般用到4根或者是8根的数据线,这样的方式会占用较多的单片机I/O口,影响微处理器的使用,PCF8574和PCF8574AT这两种芯片都是IIC芯片,可以将并行的八根数据线转换只用两根数据线进行控制,减少了I/O口的使用,提高所使用微处理器的控制能力,用到的这两种芯片组成的模块是由于不同的批次地址也不相同,通过查这两种芯片的数据手册发现,这两芯片的地址是不一样的。默认的A2、A1、A0是不接的状态,处于高电平,这样就可以计算出来PCF8574T的八位二进制数01001110,地址是0x4e;PCF8574AT的八位二进制数01111110,地址是0x7e。

设备从地址刚那句官方手册的提供如下所示:

在这里插入图片描述
数据手册上也提供了数据的写模式如下所示:
在这里插入图片描述
数据读取如下所示:
在这里插入图片描述

同时还说明了我们前面讲到的中断(这里我们后面的实验用不到,可以不用关注)

PCF8574 提供一个可以连接到 MCU 对应输入端的开漏输出口( INT)。这样可使 PCF8574 能够启动系统中另外一处的动作。在输入模式中,输入信号的上升或下降沿产生中断。在时间 tiv 之后 INT 有效。当口数据变为初始值或产生中断端口的数据写入/读出时,中断电路复位并重新激活。在下列条件下发生复位:

  • 读模式中, SCL 信号上升沿之后的应答位
  • 写模式中, SCL 信号从高到低的跳变之后的应答位
  • 应答时钟脉冲期间的中断复位可能会导致中断的丢失

中断复位后 I/O 口的每个变化都会被检测,并在下一个时钟上升沿作为 INT 发送。对另一个器件的
读写不影响中断电路。

示意图如下所示:

在这里插入图片描述

下面是我们常用的准双向I/O口的功能

准双向 I/O 口可用作输入和输出而不需要通过控制寄存器定义数据的方向。 上电时 I/O 口为高电平。该模式中只有 VDD 提供的电流有效。在大负载输出时提供额外的强上拉以使电平迅速上升。当输出写为高电平时打开强上拉,在 SCL 的下降沿关闭。I/O 口用作输入之前应当为高电平。

2、使用PCF8574驱动LCD1602

模块原理图如下所示:
在这里插入图片描述
不同模式的信号真值表
在这里插入图片描述
关于具体数据这里还是看手册吧
在这里插入图片描述
在这里插入图片描述
完整代码如下:

#include "iic.h"

//#define SCL_HIGH HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET)
//#define SCL_LOW HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET)
//#define SDA_HIGH HAL_GPIO_WritePin(SCL_GPIO_Port, SDA_Pin, GPIO_PIN_SET)
//#define SDA_LOW HAL_GPIO_WritePin(SDA_GPIO_Port, SDA_Pin, GPIO_PIN_RESET)

// 这样就是存在一定可能被编译器优化掉,其他无影响,为直接使用寄存器来控制

#define SCL_HIGH SCL_GPIO_Port->BSRR = SCL_Pin
#define SCL_LOW SCL_GPIO_Port->BRR = SCL_Pin
#define SDA_HIGH SDA_GPIO_Port->BSRR = SDA_Pin
#define SDA_LOW SDA_GPIO_Port->BRR = SDA_Pin


#define fac_us 72   //时钟频率,单位MHZ

/*微秒级延时函数*/
void delay_us(uint32_t nus)
{
	uint32_t ticks;
	uint32_t told,tnow,tcnt=0;
	uint32_t reload=SysTick->LOAD;			//LOAD的值
	ticks=nus*fac_us; 						//需要的节拍数
	told=SysTick->VAL;        				//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;
		if(tnow!=told)
		{
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}
	}
}


void I2C_SDA_Mode(uint8_t addr) //数据线输入输出
{
	GPIO_InitTypeDef GPIO_InitStruct;

	if(addr) //为1的时候是输出模式
	{
		  GPIO_InitStruct.Pin = SDA_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
		  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
	else // 为0的时候上拉输入
	{
		  GPIO_InitStruct.Pin = SDA_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
		  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
}

//启动信号
void I2C_Start(void)
{
	I2C_SDA_Mode(1);//配置为输出模式

	SCL_HIGH;
	SDA_HIGH;
	delay_us(5);

	SDA_LOW;
	delay_us(5);
	SCL_LOW;
}
//停止信号
void I2C_Stop(void)
{
	I2C_SDA_Mode(1);//配置为输出模式

	SDA_LOW;
	delay_us(5);
	SCL_HIGH;
	delay_us(5);
	SDA_HIGH;
}
uint8_t I2C_Write_Ack(void)//写应答
{
	uint8_t timeACK;

	I2C_SDA_Mode(0);//配置为输入模式

	SCL_HIGH;
	delay_us(2);

	while(HAL_GPIO_ReadPin(SDA_GPIO_Port, SDA_Pin)) //等待应答
	{
		if(++timeACK > 250)//避免死机,加入避免长时间不应答的函数
		{
			I2C_Stop();
			return 1;
		}
	}
	SCL_LOW;//正常收到应答信号,拉低
	delay_us(2);

	return 0;
}
void I2C_Send_Ack(uint8_t ack)
{
	I2C_SDA_Mode(1);

	if(ack)
		SDA_HIGH;
	else
		SDA_LOW;

	SCL_HIGH;
	delay_us(4);
	SCL_LOW;
	delay_us(4);
}

void I2C_Write_Byte(uint8_t data)
{
	SCL_LOW;//时钟为低的时候才可以开始写数据
	delay_us(2);

	for(uint8_t i = 0;i<8;i++)//循环八次
	{
		I2C_SDA_Mode(1);//配置为输出模式

		if((data<<i) & 0x80)//判断是0还是1
			SDA_HIGH;
		else
			SDA_LOW;

		SCL_HIGH;
		delay_us(2);
		SCL_LOW;
		delay_us(2);
	}
}
uint8_t I2C_Read_Byte(void)
{
	uint8_t data;
	for(uint8_t i = 0;i<8;i++) //循环八次
	{
		I2C_SDA_Mode(0); //配置为输入模式

		SCL_HIGH;
		delay_us(2);

		data <<= 1; //数据一位位的往前读
		if(HAL_GPIO_ReadPin(SDA_GPIO_Port, SDA_Pin) == GPIO_PIN_SET)
		{
			data |= 0x01;
		}
		SCL_LOW;
		delay_us(2);
	}
	return data;
}

在主函数中调用:
在这里插入图片描述
将程序下载到开发板,效果如下所示
在这里插入图片描述
如果采用硬件iic:

#include "PCF8574.h"
#include "iic.h"

extern I2C_HandleTypeDef hi2c1;
#define SLAVE_ADDRESS_LCD  0x4E

void lcd_send_cmd(char cmd)
{
	char data_u, data_l;
	uint8_t i2c_frame_data[4];
	data_u = (cmd&0xf0);
	data_l = ((cmd<<4)&0xf0);
	i2c_frame_data[0] = data_u|0x0C;  //en=1, rs=0
	i2c_frame_data[1] = data_u|0x08;  //en=0, rs=0
	i2c_frame_data[2] = data_l|0x0C;  //en=1, rs=0
	i2c_frame_data[3] = data_l|0x08;  //en=0, rs=0
	HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) i2c_frame_data, 4, 100);
	HAL_Delay(1);
}

void lcd_send_data(char data)
{
	char data_u, data_l;
	uint8_t i2c_frame_data[4];
	data_u = (data&0xf0);
	data_l = ((data<<4)&0xf0);
	i2c_frame_data[0] = data_u|0x0D;  //en=1, rs=0
	i2c_frame_data[1] = data_u|0x09;  //en=0, rs=0
	i2c_frame_data[2] = data_l|0x0D;  //en=1, rs=0
	i2c_frame_data[3] = data_l|0x09;  //en=0, rs=0
	HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS_LCD, (uint8_t *) i2c_frame_data, 4, 100);
	HAL_Delay(1);
}

void lcd_clear (void)
{
	lcd_send_cmd (0x01);
	HAL_Delay(1);
}

void lcd_put_cur(int row, int col)
{
	switch(row)
	{
	case 0:
		col |= 0x80;
		break;
	case 1:
		col |= 0xC0;
		break;
	}
	lcd_send_cmd(col);
}

void lcd_init(void)
{
	lcd_send_cmd(0x33);
	lcd_send_cmd(0x32);
	lcd_send_cmd(0x28);
	lcd_send_cmd(0x06);
	lcd_send_cmd(0x0C);
	lcd_send_cmd(0x01);
	lcd_send_cmd(0x80);
}

void lcd_send_string (char *str)
{
    while(*str)
    {
    	lcd_send_data(*str++);
    }
	HAL_Delay(1);
}

之后在主函数中调用如下
在这里插入图片描述

将程序下载到开发板,效果如下所示:
在这里插入图片描述

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
STM32 PCF8574是基于STM32微控制器的外设模块,其实际上是一个I2C IO扩展芯片。PCF8574具有8个IO引脚,可通过I2C总线与STM32微控制器通信。在STM32上使用PCF8574,我们可以通过I2C协议来控制和读取外部设备,从而有效扩展了STM32的IO口数量。 在使用STM32 PCF8574之前,我们需要先进行一些初始化配置。首先,我们需要配置STM32的I2C外设。我们要设置I2C通信速率和I2C地址模式,然后使能I2C,以便和PCF8574建立通信。接下来,我们可以使用相关的I2C库函数来进行I2C数据读写操作。 要控制PCF8574的GPIO输出,我们可以使用I2C库函数发送相应的命令和数据。比如,我们可以发送一个写命令和数据到PCF8574,使其输出某种电平信号。通过控制PCF8574的GPIO输出,我们可以控制外部设备,比如LED灯、继电器等。 同时,我们可以通过I2C库函数读取PCF8574的输入状态。通过读取PCF8574的输入状态,我们可以获取外部设备产生的信号,比如按键、开关等。 需要注意的是,使用PCF8574扩展IO时,由于使用了I2C总线,通信速度会相对较慢,因此需要合理规划IO资源和优化程序。此外,还需要注意I2C地址冲突的问题,确保在系统中所有I2C设备的地址唯一。 总之,STM32 PCF8574是一个非常实用的外设模块,能够帮助扩展STM32的IO口数量,实现更多的应用需求。它的使用主要涉及到STM32的I2C外设的配置和相关的数据读写操作,能够满足我们对外部设备控制和状态读取的需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃成蹊2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值