我这块板子很多芯片都是IIC总线传输,所以先去学了下IIC,然后继续操作芯片。(初学者经验不足,在淘宝上看他们给的截图包含大量的视频,实际结果是只有简单的实验有教程,其他的直接给代码,悲哀啊!!!)
网上可以很容易找到IIC总线的中文资料,我这里就不在啰嗦,我简单的把IIC的操作封装下,方便使用。
1、D/A实验:
(D/A芯片是 PCF8591,目的是控制LED灯缓慢熄灭)
#include "iic.h" // 引入IIC操作,自定义的库 #include <reg52.h> void main() { uchar num; IIC_INIT(); // 初始化IIC总线 while(1) { write_byte(0x90,0x40,num); // 0x90 为PCF8591地址,0x40 为控制命令, num 为转换数据 num ++; delay(20); } }
2、EEPROM实验
(EEPROM芯片采用 AT24C02A,目的是向芯片内部某一个地址写入一个字节,然后读出该数据送给8个发光二极管)
#include "iic.h" #include <reg52.h> void main() { IIC_INIT(); // 初始化IIC总线 write_byte(0xa0,10,0x55); // 向芯片地址10,写入数据0x55 delay(20); // 延时20ms P1 = read_byte(0xa1,10); // 从芯片地址10,读出数据并送入P1口,P1接了8个发光二极管 while(1); }
主程序写起来非常简单,所有操作都封装在iic.c中
IIC.h
#ifndef __IIC_H__ #define __IIC_H__ #include "delay.h" void IIC_INIT(); // 器件初始化 void write_byte(uchar add,uchar command1,uchar command2); // 写一个字节 uchar read_byte(uchar add,uchar command1); // 读一个字节 #endif // IIC_INIT(); // write_byte(add,command1,command2); // temp = read_byte(add,command1);
IIC.c
#include <reg52.h> #include "IIC.h" void extra(); // 我这个板子必须调用,因为有干扰 void start(); // IIC 启动信号 void stop(); // IIC 停止信号 void ack(); // IIC 应答信号 void noack(); // IIC 非应答信号 void iicwr_byte(uchar); // IIC 写一个字节 uchar iicre_byte(); // IIC 读一个字节 sbit RST=P2^5; //时钟 不需要,可以关掉 sbit CS_DA =P2^4 ; //可以关掉 sbit sda = P2^0; // 两个控制端 sbit scl = P2^1; // 两个控制端 void extra() // 这个函数我的板子必须用,因为会影响我的实验 { RST=0; //总线时钟低电评 CS_DA =0; } void IIC_INIT() { extra(); sda = 1; scl = 1; } void iicwr_byte(uchar dat) { uchar i; scl = 0; for (i = 0;i < 8;i ++) { if (dat & 0x80) { sda = 1; }else{ sda = 0; } dat = dat << 1; delay_us(5); scl = 1; delay_us(5); scl = 0; delay_us(5); } sda = 1; delay_us(5); } uchar iicre_byte() { uchar i,dat; scl = 0; delay_us(5); sda = 1; // 释放数据总线,以便器件回传数据,一旦为低电平,由于线与关系,将无法操作 delay_us(5); for (i = 0;i < 8;i ++) { scl = 1; delay_us(5); dat <<= 1; if (sda) { dat ++; } scl = 0; delay_us(5); } return dat; } void start() // 起始信号 { sda = 1; delay_us(5); scl = 1; delay_us(5); sda = 0; delay_us(5); } void stop() // 停止信号 { sda = 0; delay_us(5); scl = 1; delay_us(5); sda = 1; delay_us(5); } void ack() // 应答 { uchar i; scl = 1; delay_us(5); while((sda == 1) && (i < 200)) i++; // 等待应答信号一定时间 scl = 0; delay_us(5); } void noack() // 非应答 { sda = 1; delay_us(5); scl = 1; delay_us(5); scl = 0; delay_us(5); } uchar read_byte(uchar add,uchar command1) { uchar temp; uchar tt = add & 0xfe; // add 最低位1表示传回,这里把他最低位变为0,因为第一步总是先发送器件地址(方向位为0) IIC_INIT(); start(); // 起始信号 iicwr_byte(tt); // 器件地址 ack(); iicwr_byte(command1); // 命令1,对于24C02表示内部地址;对于PCF8591表示控制命令 ack(); start(); // 再来起始信号,表示我要改变方向拉 iicwr_byte(add); // 在传器件地址,add 最低位为1表示,下次回传数据 ack(); temp = iicre_byte(); noack(); stop(); return temp; } void write_byte(uchar add,uchar command1,uchar command2) { IIC_INIT(); start(); iicwr_byte(add); // 器件地址 ack(); iicwr_byte(command1); // 命令1,对于AT24C02表示内部地址;对于PCF8591表示控制命令 ack(); iicwr_byte(command2); // command2为数据拉 ack(); stop(); }
关于delay.c就是两个延时函数,也写成一个文件,方便调用:
delay.h
#ifndef __DELAY_H__ #define __DELAY_H__ #define uchar unsigned char #define uint unsigned int void delay_us(uchar us); // us级别 void delay(uint ms); // ms级别 #endif
delay.c
//#include <reg52.h> #include "delay.h" void delay_us(uchar us) { while(--us); } void delay(uint ms) { uint x,y; for (x = ms * 32;x > 0;x--) for(y = 2;y > 0;y--); }