/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
过程:
1. 初始化 ——> 2.ROM跳过指令 ——> 3.功能指令
功能指令:
温度转化参数
读取寄存器
由上面总结的流程:
1. 初始化
2. ROM(0xcc命令)
3. 读取温度转化函数(0x44)
4. 初始化
5. ROM
6. 读取寄存器
7. 先读低八位
8. 再读高八位
10. 精度处理
原理引脚图:
根据引脚图的到我们所需要设置的引脚和头文件
#include
#include
#include "ds1302.h"
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3;
同时,得到代码:
float rd_temperature(void) //浮点数
{
unsigned float temp;
unsigned char low,high; //高八位与低八位
init_ds18b20() //第一部初始化 1.Initialization
Write_DS18B20(0xcc); //2.ROM 跳过指令
Write_DS18B20(0x44); //3.功能指令
/* 题目说每次上电都需要初始化一次 */
init_ds18b20() //第一部初始化 1.Initialization
Write_DS18B20(0xcc); //2.ROM 跳过指令
Write_DS18B20(0xbe); //3.功能指令
/* 接下来就可以读 */
low = Read_DS18B20();
high = Read_DS19B20();
/* 返回一个值 */
temp = ((high << 8)|low)/16.0; // 可以选择 *0.0625
return temp;
}
二、AT24C02_EEPROM储存器
官方所给源码(IIC)
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
/* 根据引脚*/
#include
#include "intrins.h"
sbit sda = P21;
sbit scl = P20;
#define DELAY_TIME 5
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
2. 器件地址
3. 字节写入流程
我们根据图八就可以明白整个流程
开始 -> 发送(写)器件地址 -> 等待 -> 发送字节地址 -> 等待 -> 写数据 -> 等待 -> 停止
其中 写器件地址为(0xa0),读器件地址为(0xa1)
4. EEPROM代码
void EEPROM_Write(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
{
I2CStart(); //1.开始
I2CSendByte(0xa0); //2.写入器件地址
I2CWaitAck(); //3.等待
I2CSendByte(addr); //3.写入字节地址 ,这个地方使用的时候一般为0
I2CWaitAck();//4 等待
/* I2CSendByte(dat); // 5.数据发送
I2CWaitAck(); */
// 上面是发送单个,我们可以改为发送整个数组长度,用num计数,一个个发
while(num--)
{
I2CSendByte(EEPROM_String);
I2CWaitAck();
I2C_Delay(200); //补充数据
}
I2CStop();
}
5. 随机读流程
我们就可以得到整个流程
开始 -> 发送(写)器件地址 -> 等待 -> 发送字节地址 -> 等待 -> 开始 -> 发送(读)器件地址 -> 等待 -> 读数据 -> 停止
6. 随机读Read代码
void EEPROM_Read(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
{
I2CStart(); //1.开始
I2CSendByte(0xa0); //2.写入器件地址
I2CWaitAck(); //3.等待
I2CSendByte(addr); //3.写入字节地址
I2CWaitAck();//4 等待
I2CStart();
I2CSendByte(0xa1); //修改这个地址
I2CWaitAck();
/* I2CSendByte(dat); // 5.数据发送
I2CWaitAck(); */
// 上面是发送单个,我们可以改为发送整个数组长度,用num计数,一个个发
/* 下面这个读数据可以实现整个字符全部读取,是需要记住的 */
while(num--)
{
*EEPROM_String++=I2CReceiveByte();
if(num) I2CSendAck(0); //是1,说明有消息,发送应答
else I2CSendAck(1); /是0.说明没消息,不应答
}
I2CStop();
}
三、PCF8591模块
1. 原理图及引脚
两个模块都是IIC模块里的,所以我们的引脚都是一样的
代码如下:
#include "reg52.h"
#include "intrins.h"
sbit sda = P2^1;
sbit scl = P2^0;
上面说过两者都是在IIC里面的,所以通信协议是相同的,我们就可以使用同样的流程来进行读写操作,同时,我们不需要对写内容进行修改(同时读取后,我们使用比例对参数进行修改,让其符合我们的要求)
代码如下:
Ad_Read 读取函数
unsigned char Ad_Read(unsigned char addr)
{
unsigned char temp;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte(); //接收消息
I2CSendAck(1); //答应
I2CStop(); //停止
return temp; //返回AD值
}
Da_Write 写入函数
void Da_Write(unsigned char dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x43); //选择需要写入器件的地址 0x41与0x43,写入的时候为
I2CSendByte(dat); //发送数据
I2CWaitAck();
I2CStop();
}
四、DS1302时钟模块
他的协议是SPI协议,我们的头文件是使用的是onewrie.h,单总线
1. 官方所给底层
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
2. 原理图及引脚
由上面的原理图,我们就可以得到其头文件与引脚
#include "ds1302.h"
#include
#include
sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;
通过上面的,我们就知道如何写入和读取时间数据
我们根据表格上的 WRITE,与READ一列,其之后对应了各个时间
很多时候我们不需要到 日月周年,我们接着把需要的时间设置到一个数组里:ucRtc[]
所以我们得到一个流程:
code unsigned char write_addr[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
code unsigned char read_addr[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
code unsigned char ucRtc[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void Set_Rtc()
{
unsigned char i;
Write_Ds1302_Byte(0x8e,0x00);// 关闭写保护
for(i = 0;i<7;i++)
{
Write_Ds1302_Byte(write_addr[i],ucRtc[i]); //每位时间地址写入需要时间
}
Write_Ds1302_Byte(0x8e,0x80);// 开启保护
}
void Read_Rtc()
{
unsigned char i;
for(i=0;i<7;i++)
ucRtc[i] = Read_Ds1302_Byte(read_addr[i]);
}
这是在我们设置好数组的需要填的时间参数后进行的,如果我们需要把时间放在主函数设置,我们可以把ucRtc的位置不设置,而使用指针代替
unsigned char* ucRtc
我们也可以和上面一样直接在onewire里面写入即可,我们也可以不写,只需要在定义里面添加,在主函数写即可
修改如下:
void Set_Rtc(unsigned char* ucRtc)
{
unsigned char i;
Write_Ds1302_Byte(0x8e,0x00);// 关闭写保护
for(i = 0;i<7;i++)
{
Write_Ds1302_Byte(write_addr[i],ucRtc[i]); //每位时间地址写入需要时间
}
Write_Ds1302_Byte(0x8e,0x00);// 开启保护
}
void Read_Rtc(unsigned char* ucRtc)
{
unsigned char i;
for(i=0;i<7;i++)
ucRtc[i] = Read_Ds1302_Byte(read_addr[i]);
}
通过滑动变阻器来改变输出频率
我们就需要选择定时器的计数功能,但是因为定时和计数只能选一个,所以我们使用两个中断,一个计时(定时器1)一个计数(定时器0)
我们直接来看他的代码:
定时器 与下多一个 0x05,且不需要打开中断使能
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x05; //设置计数模式
TL0 = 0; //设置定时初始值
TH0 = 0; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
然后在计时下算频率
void Timer1Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //定时器中断1打开
EA = 1; //总中断打开
}
/* 定时器1中断服务函数 */
void Timer1Server() interrupt 3
{
if(++Timer_1000Ms == 1000) //实时读取频率值
{
Timer_1000Ms = 0;
Freq = TH0 << 8 | TL0; //和时间函数很相似
TH0 = TL0 = 0;
}
}