打印

float型数据通过AT24C02存取的问题

[复制链接]
1740|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
502650182|  楼主 | 2017-5-7 17:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
将一个float型数据存到AT24C02中,再读出到串口显示,为啥串口显示的是一个错误的数呀?是我程序哪有问题吗?请各位大神指点。谢谢
#include<reg51.h>
#include <stdio.h>

#define write_c02 0xa0
#define read_c02 0xa1

typedef unsigned char uchar;
typedef unsigned int uint;

sbit sda = P2^0;
sbit scl = P2^1;

/*********延时**************/
void delay()           //约5us
{
    ;;
}

void delayms(uint xms)  //延时X毫秒
{
    uchar x,y;
        for(x=xms;x>0;x--)
            for(y=110;y>0;y--);        
}

/**********I2C子程序***************/
void i2c_init()          //I2C初始化
{
    sda=1;
        delay();
        scl=1;
        delay();
}

void start()      //启动I2C
{
    sda=1;
        scl=1;
        delay();    //在scl为高电平时,sda一个下降沿为启动信号
        sda=0;     
        delay();
}

void stop()        //停止I2C
{
    sda=0;
        scl=1;
        delay();
        sda=1;          //在scl为高电平时,sda一个上升沿为停止信号
        delay();
}

void ack()       //应答信号0
{
    uchar i=0;   //等待变量
        scl=1;       //在scl为高电平期间等待应答
        delay();
        while((sda=1)&&i<250)//若为应答0即退出,从机向主机发送应答信号
        i++;
        scl=0;       //应答之后将scl拉低
        delay();
}

void nack()      //非应答
{
    scl=1;       //在scl为高电平期间,由主机向从机发送一个1;非应答信号
        delay();
        sda=1;
        scl=0;       //应答之后将scl拉低
        delay();
}

void send_byte(uchar date)  //写一个字节
{
    uchar i , temp;
        temp=date;     //存入要写入的数据,即要发送到sda上的数据
        for(i=0;i<8;i++)
        {
            temp<<=1;
                scl=0;     //只有在scl为低电平时,才允许sda上的数据变化
                delay();
                sda=CY;    //将CY里的数据发送到sda数据线上
                delay();
                scl=1;     //在scl为高电平时,不允许sda上的数据变化,使数据稳定
                delay();
                scl=0;     //允许sda数据线的数据变化,等待下一个数据的传输
                delay();
        }//发送完一个字节数据后主机要等待从机的应答
        scl=0;//允许sda变化
        delay();
        sda=1;//sda拉高等待应答,当sda=0时,表示从机应答
        delay();
}                       

uchar read_byte()        //读一个字节数据
{
    uchar i,j,k;
        scl=0;         //读之前先允许sda变化
        delay();       //等待数据
        for(i=0;i<8;i++)
        {
            scl=1;     //不允许sda变化
                delay();
                j=sda;     //读出sda上的数据
                k=(k<<1)|j;//将数据通过|运算存入K中
            delay();
                scl=0;     //允许sda变化等待下一位数据的到来
                delay();
        }
        delay();
        return k;      //返回读出的数据
}

/*****************从AT24C02中存取数据***********************/
void write_at24c02(uchar address,uchar date)//在at24c02中的指定地址写入数据、
{
    start();         //启动I2C
        send_byte(write_c02); //写入期间地址和写操作
        ack();        //从机应答0
        send_byte(address);//写入写数据的单元地址
        ack();
        send_byte(date);//在指定地址中写入数据
        ack();
        stop();         //停止I2C
}

uchar read_at24c02(uchar address)//在at24c02的指定地址中读出写入的数据
{
    uchar dat;   //用来存储读出的数据
        start();     //启动I2C
        send_byte(write_c02);//写入at24c02期间地址和写操作
        ack();
        send_byte(address);  //写入要读取at24c02的数据的单元地址
        ack();
        start();          //再次启动I2C
        send_byte(read_c02);//写入at24c02期间地址和读操作
        ack();
        dat=read_byte();//读出指定地址中的数据
        nack();//主机发出非应答1
        stop();//停止I2C
        return dat;
}

/**********************从AT24C02中存取float型数据****************************/
void write_f_rom(uchar address,float date)//将系数存入AT24C02
{
    unsigned long d;
        unsigned char d0,d1,d2,d3;
        d=(unsigned long)(date);
        d0=(unsigned char)(d>>24);
        d1=(unsigned char)((d&0x00ff0000)>>16);
        d2=(unsigned char)((d&0x0000ff00)>>8);
        d3=(unsigned char)(d%256);
        delayms(10);
        write_at24c02(address,d0);
        delayms(10);
        write_at24c02(address+0x01,d1);
        delayms(10);
        write_at24c02(address+0x02,d2);
        delayms(10);
        write_at24c02(address+0x03,d3);
        delayms(10);
}

float read_f_rom(unsigned char address)        //从AT24C02中读取数据
{
    unsigned char i;
        float date;
        unsigned char c[4];
        unsigned long d[4];

        for(i=0;i<4;i++)
        {
            read_at24c02(address+i);
                c[i]=read_at24c02(address+i);      
        }
    for(i=0;i<4;i++)
        {
            d[i]=(unsigned long)c[i];
        }
        date=((float)((long)((d[0]<<24)+(d[1]<<16)+(d[2]<<8)+d[3])));
        return date;
}

/******************串口初始化**********************/

void uart_init(void) //串口的初始化
{
    TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2
    TH1=0xfd;//设置波特率为9600
    TL1=0xfd;
    TR1=1;//启动定时器/计数器1        
    SCON=0x50; //0101 0000.串口工作方式1,允许串行控制
    PCON=0x00;//设置SMOD=0
    IE=0x90; //CPU允许中断,串行允许中断     
    TI=1;//直接使用printf必须加入此句才能实现发送
        ES=1;//开串口中断
        EA=1;//开总中断
}

void main()
{
//    uchar i;
        float shuju=1.1;
        float shuju1;
        i2c_init();
        uart_init();
        start();
        while(1)
        {
                write_f_rom(0x00,shuju);
                delayms(100);
                shuju1=read_f_rom(0x00);
                delayms(100);
                printf("%.1f\n",shuju1);
                delayms(200);
        }
}

相关帖子

沙发
linqing171| | 2017-5-7 19:55 | 只看该作者
应该需要在时钟上升之前。无论等,还是发。,当然,这不是问题。
while((sda=1)&&i<250)  这里有个等号要变== ,当然,这也不会有问题。

sda=CY;    这个真是艺高人胆大啊,万一你将来修改delay函数怎么办?,当然,这也不是问题所在。

你的问题所在是你的float转long的时候丢失了低位。

void write_f_rom(uchar address,float date)//将系数存入AT24C02
{
        unsigned char *d=(unsigned char*)&date;
write_at24c02(address,d[0]);
        delayms(10);
        write_at24c02(address+0x01,d[1]);
        delayms(10);
        write_at24c02(address+0x02,d[2]);
        delayms(10);
        write_at24c02(address+0x03,d[3]);
        delayms(10);
}

float read_f_rom(unsigned char address)        //从AT24C02中读取数据
{
        unsigned char c[4];

        for(i=0;i<4;i++)
        {
                c[i]=read_at24c02(address+i);      
        }
return *(float*)c;
}

使用特权

评论回复
板凳
Prry| | 2017-5-7 23:07 | 只看该作者
不用这么麻烦,浮点数占4个字节(32位系统),不用关心浮点数内存的存储方式,增加一个操作多字节的函数吧,假如增加一个写/读多字节的函数——write_bytes_at24c02(u8 addr,u8 *data,u8 len)和read_bytes_at24c02(u8 addr,u8 *data,u8 len),具体自行实现,这个简单。然后可以这样写浮点数:
write_bytes_at24c02(address+0x01,(unsigned char*)&date,sizeof(float));
float read_date;
read_bytes_at24c02(address+0x01,(unsigned char*)&read_date,sizeof(float));
根本不需拆解浮点数,也不用理会大端小端的内存存储方式,而且double型,甚至一个大的结构体也可以这样读写。比如有有个结构体,存放的是一个仪器的设置参数(注意结构体字节对齐规则):
struct param
{
...
};
那么可以这样调用:
struct param test_param;
write_bytes_at24c02(address+0x01,(unsigned char*)&test_param,sizeof(struct param ));
read_bytes_at24c02(address+0x01,(unsigned char*)&test_param,sizeof(struct param ));

使用特权

评论回复
地板
ningling_21| | 2017-5-8 09:47 | 只看该作者
用个联合体 就解决

使用特权

评论回复
5
woshizhengjie89| | 2017-5-8 11:39 | 只看该作者
51不支持浮点数

使用特权

评论回复
6
coody| | 2017-5-8 13:44 | 只看该作者

不是吧?我51也经常的用浮点运算啊。。。。
KEIL C51支持浮点库的哦。

使用特权

评论回复
7
coody| | 2017-5-8 13:46 | 只看该作者
由于读写AT24CXX的函数我都是按字节操作,所以其它格式的数据,我都是强转实现,简单方便,不必理会大小端。

使用特权

评论回复
8
woshizhengjie89| | 2017-5-8 14:08 | 只看该作者
coody 发表于 2017-5-8 13:44
不是吧?我51也经常的用浮点运算啊。。。。
KEIL C51支持浮点库的哦。

那有空我在51开发板上跑下你的程序试试

使用特权

评论回复
9
tomzbj| | 2017-5-8 14:29 | 只看该作者
通过多字节操作或者用联合体都可以...

不过工业上更常用的方式是根据需要的小数位数, 乘以1000或者100000之类, 然后按整数储存或者通讯.
储存还好, 通讯的话可能会有一方不是标准的IEEE754, 这样用浮点数直接传输就不准了. 用整数传输就没这个问题.

使用特权

评论回复
10
cool_coder| | 2017-5-8 15:03 | 只看该作者
Prry 发表于 2017-5-7 23:07
不用这么麻烦,浮点数占4个字节(32位系统),不用关心浮点数内存的存储方式,增加一个操作多字节的函数吧 ...

同意这位兄弟的意见。串行EEPROM的调用接口应该就是这样的,封装好后和操作文件或者内存感觉差不多。另外,C程序中善用指针很重要,用好了程序效率会很高,否则可能是灾难。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

10

帖子

0

粉丝