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

前言

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

硬件略解

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

image-20201020211249436

image-20201020211307767

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;
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值