两天速成蓝桥杯单片机之IIC外设PCF8591和EEPROM AT24C02(5)

it2023-10-11  72

前言

蓝桥杯的这些通信协议的外设好像会难倒很多人,但是实际上是没有那么难的,基本上记住这些芯片的套路就能搞明白怎么去写。

硬件略解

这里就不略解硬件啦要,因为都是和上一章的DS18B20一样很简答的硬件连接方式。值得注意的一点是芯片的连接方式决定了器件的IIC地址,这一点在芯片手册是有提到的

A0 A1 A2都是负责这件事的。

软件详解

其实软件发生了很多奇奇怪怪的事情导致我调IIC,花费了较多的时间,其实很多时候都不是IIC的时序问题是我们操作器件的问题。

下面是IIC.c的文件,其实我的操作就是把他的软件延时改成了33个nop(我看到很多资料让我这么改,我朋友告诉我不改他也调通了,很迷),以及在最后加了一个IIC_Ack(bit ackbit)函数。

/* 程序说明: IIC总线驱动程序 软件环境: Keil uVision 4.10 硬件环境: CT107单片机综合实训平台 8051,12MHz 日 期: 2011-8-9 */ #include "intrins.h" #include "iic.h" #include "common.h" #define somenop \ { \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ _nop_(); \ } //总线引脚定义 sbit SDA = P2 ^ 1; /* 数据线 */ sbit SCL = P2 ^ 0; /* 时钟线 */ void IIC_Delay(unsigned char i) { do { _nop_(); } while (i--); } //总线启动条件 void IIC_Start(void) { SDA = 1; SCL = 1; somenop; SDA = 0; somenop; SCL = 0; } //总线停止条件 void IIC_Stop(void) { SDA = 0; SCL = 1; somenop; SDA = 1; somenop; } //发送应答 void IIC_SendAck(bit ackbit) { SCL = 0; SDA = ackbit; // 0:应答,1:非应答 somenop; SCL = 1; somenop; SCL = 0; SDA = 1; somenop; } //等待应答 bit IIC_WaitAck(void) { SDA = 1; somenop; SCL = 1; somenop; if (SDA) { SCL = 0; IIC_Stop(); return 0; } else { SCL = 0; return 1; } } //通过I2C总线发送数据 void IIC_SendByte(unsigned char byt) { unsigned char i; for (i = 0; i < 8; i++) { SCL = 0; somenop; if (byt & 0x80) SDA = 1; else SDA = 0; somenop; SCL = 1; byt <<= 1; somenop; } SCL = 0; } //从I2C总线上接收数据 unsigned char IIC_RecByte(void) { unsigned char i, da; for (i = 0; i < 8; i++) { SCL = 1; somenop; da <<= 1; if (SDA) da |= 1; SCL = 0; somenop; } return da; } void IIC_Ack(bit ackbit) { if (ackbit) { SDA = 0; } else { SDA = 1; } somenop; SCL = 1; somenop; SCL = 0; SDA = 1; somenop; }

以及接下来的是PCF8591,也就是AD/DA芯片的使用,注意一点的是,这芯片一次只能使用AD和DA功能中选一个。比如说你想让DA一直输出个3v,还要读一下AD,那个3v就会消失。

#include "PCF8591.h" //PCF8591 8位adc 基准5v //转换公式为dat/255*5 uchar Read_AINX(uchar num) { uchar dat; IIC_Start(); //IIC总线起始信号 IIC_SendByte(0x90); //PCF8591的写设备地址 IIC_WaitAck(); //等待从机应答 IIC_SendByte(num); //写入PCF8591的控制字节 IIC_WaitAck(); //等待从机应答 IIC_Stop(); //IIC总线停止信号 IIC_Start(); //IIC总线起始信号 IIC_SendByte(0x91); //PCF8591的读设备地址 IIC_WaitAck(); //等待从机应答 dat = IIC_RecByte(); //读取PCF8591通道3的数据 IIC_Ack(0); //产生非应答信号 IIC_Stop(); //IIC总线停止信号 return dat; } void SetDACOut(uchar val) { IIC_Start(); IIC_SendByte(0x90); IIC_WaitAck(); IIC_SendByte(0x40); IIC_WaitAck(); IIC_SendByte(val); IIC_WaitAck(); IIC_Stop(); }

EEPROM存储芯片用起来也很简单。需要注意的是EEPROM的写单个字节的速度较慢,每次写大概需要耗时5ms,所以说如果你写完立刻读是读不到的。

#include "eeprom.h" //@参数:add是写的地址,从0x00-0xff可以写 //@参数:data是写的数据 bit write_24c02(unsigned char add, unsigned char date) { uchar ack; EA = 0; IIC_Start(); IIC_SendByte(0xa0); IIC_WaitAck(); IIC_SendByte(add); IIC_WaitAck(); IIC_SendByte(date); ack = IIC_WaitAck(); IIC_Stop(); EA = 1; return ack; } unsigned char read_24c02(unsigned char add) { unsigned char temp; EA = 0; IIC_Start(); IIC_SendByte(0xa0); //发送器件地址 IIC_WaitAck(); IIC_SendByte(add); //发送要操作的地址 IIC_WaitAck(); IIC_Stop(); IIC_Start(); IIC_SendByte(0xa1); //发送读操作 IIC_WaitAck(); temp = IIC_RecByte(); //读一字节 IIC_Ack(0); IIC_Stop(); EA = 1; return temp; }
最新回复(0)