sde_arm9 发表于 2010-10-17 14:14:49

m051的i2c读写24cxx,如何连续读?

点击此处下载 ourdev_590514B9D7K3.zip(文件大小:388K) (原文件名:AN_1009_IIC_for_24LC64 V1.00.zip)
这是芯唐官方的应用笔记。有单字节读写代码,连续读取一直没能实现,大家看看。

//resive data
I2C0->DATA = 0X00;         //接收数据为何要先写数据???????
DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0);//clr si
while( I2C0->CON.SI == 0 );   //poll si flag

machao 发表于 2010-10-17 15:38:01

呵呵,这就是我一直以来对使用“官方库函数”非常感冒的原因之一。

就拿LZ位的例子看,

1。代码中开始有:#include "Driver\DrvI2C.h",说明你用了它的“库”,否则 DrvI2C_Open()、DrvI2C_Ctrl()、DrvI2C_Close()来自何处?

2。既然有了DrvI2C_Open()、DrvI2C_Ctrl()、DrvI2C_Close(),为什么不实现DrvI2C_Write_Byte()和DrvI2C_Recive_Byte()?可见这个库只是帮助你设置寄存器,根本没有用户层面的接口。

3。因此你还是要看器件手册,了解各个位的意义和作用。否则DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)里面的几个0/1就无法确定。所以那些说使用库可以跳过低层寄存器,(32位MCU的寄存器比8位多的多,而且性能更强)让用户很快上手的说法是片面的。

4。除了看手册,你还要看它的库函数是什么功能,不了解根本也用不好的(这里已经不考虑库函数有BUG以及局限性的问题了)。
=============================================================================================================

对LZ位的例子,我没有看过其库函数到第是如何写的。根据判断,应该使用M051的硬件I2C口,不是I/O口模拟的。这个从DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)已经看出。

硬件I2C口需要一个启动工作的过程,应该在DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)中实现,它清除SI,然后启动I2C工作。建议你还是要了解这个函数的具体作用。

对于读取数据

//resive data
I2C0->DATA = 0X00;         //接收数据为何要先写数据???????
DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0);//clr si   
while( I2C0->CON.SI == 0 );   //poll si flag

我认为有两种可能。一是写个0,配合下面DrvI2C_Ctrl()启动I2C读数据过程,二是根本就是乱搬的写过程代码。但不管是为了什么,这样的NOTES,太低级了。

应该后面有一句 A = I2C0->DATA;A中就是读到的数据。

你应该先测试和实现是否能读到一个字节。如果能正确的读到一个字节,那么连续读就是这段代码的重复。

最后建议你购买我编写的AVR教程,里面有关于I2C详细的介绍和多种实现的方法,包括I/O模拟和使用硬件I2C,同时还有相应低层驱动函数如何建立,以及用户层面的使用。你掌握了方法,在M051上、在STM32上都是一样使用的。

你曾经说过经过锻炼和培训。我不知道是在何时,何地。但就今天你问的问题来讲,我不客气的说,你还需要经过一些真正的培训,才能有质的提高。

sde_arm9 发表于 2010-10-17 20:48:18

马老师
这段代码不是我写的,这是官方的文档。

读I2C->DATA实际测试时我加了,重复这段代码确实能实现读多字节,但eeprom协议并不是这样,可以连续读的,这个您知道。

单字节测试过,可以到,多字节写也可以,但多字节读不行,发现源代码多个写数据寄存器。

sde_arm9 发表于 2010-10-17 20:57:27

您写的avr教程我看过了,不过那是N年前的事了,您的端口模拟i2c我的产品还在用着呢。
记得好像端口使用上于别人不同,没来回更改输入输出方式。记不太清了。

老师教导的对,我应该加强基础,谨记了。

sde_arm9 发表于 2010-10-17 21:01:56

我认为有两种可能。一是写个0,配合下面DrvI2C_Ctrl()启动I2C读数据过程,二是根本就是乱搬的写过程代码

-------------------
测试发现如不加写数据寄存器这句,读到的是前面写的命令字0xa1,但试过先读一次,后面的就正确了,可是再启动读会跑飞。



硬件I2C口需要一个启动工作的过程,应该在DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)中实现,它清除SI,然后启动I2C工作。建议你还是要了解这个函数的具体作用。

---------------------

DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)这个是清标志
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 0)这个是发送启动位





但就今天你问的问题来讲,我不客气的说,你还需要经过一些真正的培训,才能有质的提高。   

---------------
这个我不做过多解释。
您觉得我说的内容不实,我要是再说我们的原理图,pcb,驱动层,应用层都是分开做的,您可能更不信,不过我们确实这样做的。
多说一句:技术员的项目一般都是从头做到尾,我们正好相反,必须分工,并不是我们的技术员不会,这样有利于整体发展,不让部分人控制。
我还是倡导分工的,把自己的部分做好,否则什么都做不精。这点也正是我们设计的产品不如国外的一个原因。

machao 发表于 2010-10-18 03:14:24

回sde_arm9:

看你的回贴感觉你还是有点不服气,正好我也利用这个机会深入了解一下M051。

首先我知道这段代码是官方的。不客气的讲,你(或你们的团队)是否有能自己从底层,从零开始写代码的能力我都表示怀疑。
看看你的失误吧:

1。这段代码不是针对M051的,是针对UNC系列的。尽管都是M0的内核,I2C的控制基本相同,但库函数肯定有区别的!你如果使用M051,那么应该采用M051的库,至少下面这个函数就不一样:

DrvI2C_Ctrl(I2C_PORT0, 0, 0, 1, 0)==》这个是NUC系列的,因为它支持2个或2个以上的I2C,所以参数中多了个I2C口的选择。

而我查看了官方提供的M051函数库,它的函数为 DrvI2C_Ctrl(0, 0, 1, 0)!

你到底是在用NUC还是用M051?还是有意将他们混在一起,要提高问题的难度,考考我?

2。你的主题是“m051的i2c读写”,那么使用M051,就应该看MO51相关的东西,比如M051的手册。这里不是说的那个简单的介绍,是指NuMicro M051 Series Technical Reference Manual EN.pdf。你看了几遍?至少关于I2C的部分应该全面看吧。另外官站已经有M051的支持库和例子。其中也有对I2C操作的,你参考过吗?

不管你分工负责哪个部分,至少这个部分的工作就是没有做好。这个是最低层的接口,这个做不好,上面再好也是空的。


下面具体分析一下如何连续读数据,具体你去测试。
请看下图:这个是我从NuMicro M051 Series Technical Reference Manual EN.pdf中找到的,在151页。153页有I2C主机读数据的流程。
http://cache.amobbs.com/bbs_upload782111/files_34/ourdev_590675ZUTYJK.jpg
(原文件名:未标题-1 拷贝.jpg)

请注意右上部的文字说明部分!
看到这里,明显的看出M051的I2C启动与AVR的不同,AVR是通过一个控制位启动一次新的I2C工作,而MO51是通过操作数据寄存器加上配合控制位的设置启动一次新的I2C操作。这个与我前面分析的相同“配合下面DrvI2C_Ctrl()启动I2C读数据过程”

在官方提供的例子中,我也没有找到I2C连续读操作的DEMO。根据NuMicro M051 Series Technical Reference Manual EN.pdf手册上151页和153页的解释,建议你做如下尝试:

如果你准备连续读10个数据:

//resive data   
I2C->DATA = 0X00;       //接收数据为何要先写数据?==》随便写个数据,表示操作了I2C数据寄存器,用于配合下一次的I2C启动

for(i=0;i<9;i++)
{
    DrvI2C_Ctrl(0, 0, 1, 1);   //clr si,启动一次工作 ==》收到数据返回ACK为1,此时才能连续读。这个是I2C规程规定的!   
    while( I2C0->CON.SI == 0 ); //poll si flag
    r_data = I2C->DATA;      //读数据,同时操作了I2C数据寄存器,也配合了下次启动
}                              // 这个循环只能读9个数据,因为读第10个数据后,返回ACK要为0

DrvI2C_Ctrl(0, 0, 1, 0);      //clr si,启动第10次读数据,设置读到后返回ACK=0,表示连续读结束。   
while( I2C0->CON.SI == 0 );   //poll si flag
r_data = I2C->DATA;      //读第10个数据。

=============================================================
以上供你参考。

你在4楼写到:“您写的avr教程我看过了,不过那是N年前的事了,您的端口模拟i2c我的产品还在用着呢。记得好像端口使用上于别人不同,没来回更改输入输出方式。记不太清了。”

实际你并没有认真的学习和体会,也没真正彻底的了解I2C!只是搬了段代码。后面还有使用AVR的I2C硬件接口的例子,有批量读和写的例子,你都没看。 上面的参考与AVR书中的方法是一样的,只是I2C的启动方式不同。

建议你重新仔细再看看,N年后这本书的思想和方法仍旧没有落后,比现在的许多新书或什么DEMO都要好。

jrcsh 发表于 2010-10-18 03:21:07

M051可以玩了?

    明天我也去拿个板子回来玩玩~~~~~但愿这次没烧得太快

sde_arm9 发表于 2010-10-18 08:45:55

没有考马老师的意识,您别误会,昨天在家,没有测试的代码,官方提供的都是nuc100的代码,和m051没什么区别,下面是从官方修改过来用于m051的代码
/*i2c写数据*/
void write_i2c_byte(uint8_t *byte,uint32_t address,uint8_t count)
{
        uint8_t n;
        uint16_t i;

           //send i2c start
        DrvI2C_Ctrl(1, 0, 1, 0);        //set start                                发送启动位
        while (I2C->CON.SI == 0);                        //poll si flag       
                       
        //send writer command
        I2C->DATA = 0XA0;                                        //send writer command        发送写命令
    DrvI2C_Ctrl(0, 0, 1, 0); //clr si flag
    while( I2C->CON.SI == 0 );                  //poll si flag

        //send address high
        //I2C->DATA = (address>>8)&0XFF;                                                               
        //DrvI2C_Ctrl(0, 0, 1, 1); //clr si and set ack       
        //while( I2C->CON.SI == 0 );                        //poll si flag

        //send address low
        I2C->DATA = address&0XFF;                                                                        //发送数据地址
        DrvI2C_Ctrl(0, 0, 1, 1); //clr si and set ack       
        while( I2C->CON.SI == 0 );                        //poll si flag

        for(n=0;n<count;n++){
                //send data
                I2C->DATA = *(byte+n);                                        //write data to                 发送数据
                DrvI2C_Ctrl(0, 0, 1, 1); //clr si and set ack       
                while( I2C->CON.SI == 0 );                        //poll si flag
        }

           //send i2c stop
        DrvI2C_Ctrl(0, 1, 1, 0); //send stop                                发送停止位
        //while( I2C0->CON.SI == 0 );
        for(i=0;i<60;i++);                                                                                        //延时
        //DrvI2C_Close(I2C_PORT0);                                                                        //关闭I2C
       
        for(i=0;i<6000;i++);
        for(i=0;i<6000;i++);
}
/*i2c读数据*/
void read_i2c_byte(uint8_t *byte,uint32_t address,uint8_t count)
{
        uint8_t n;
        uint16_t i;
        for(n=0;n<count;n++){
        //send i2c start
    DrvI2C_Ctrl(1, 0, 1, 0);               //set start                   发送启动位
        while (I2C->CON.SI == 0);                                //poll si flag
       
           //send writer command
        I2C->DATA = 0XA0;                                                                                   //发送写命令
    DrvI2C_Ctrl(0, 0, 1, 0);           //clr si
    while( I2C->CON.SI == 0 );                           //poll si flag
       
        //send address high
    //I2C->DATA = (address>>8)&0XFF;       
        //DrvI2C_Ctrl(0, 0, 1, 1);    //clr si and set ack
        //while( I2C->CON.SI == 0 );                           //poll si flag
   
        //send address low
        I2C->DATA = (address+n)&0XFF;                                                                   //发送地址
        DrvI2C_Ctrl(0, 0, 1, 1);    //clr si        and set ack
        while( I2C->CON.SI == 0 );                           //poll si flag
       
        //send start flag
        DrvI2C_Ctrl(1, 0, 1, 0);    //clr si and send start                        发送启动位       
        while( I2C->CON.SI == 0 );                           //poll si flag
       
        //send read command
        I2C->DATA = 0XA1;                                                                                   //发送读命令
        DrvI2C_Ctrl(0, 0, 1, 0);    //clr si
    while( I2C->CON.SI == 0 );                           //poll si flag

        //resive data
        I2C->DATA = 0XFF;
        DrvI2C_Ctrl(0, 0, 1, 0);    //clr si       
        while( I2C->CON.SI == 0 );                           //poll si flag

        *(byte+n)= I2C->DATA;                                                                   //读取数据
                //DrvI2C_Ctrl(0, 0, 1, 0); //clr si and set ack       
                //while( I2C->CON.SI == 0 );                        //poll si flag
        //send i2c stop
        DrvI2C_Ctrl(0, 1, 1, 0);    //clr si and set stop   发送停止位
        //DrvI2C_Close(I2C_PORT0);                           //关闭I2C
        }
}

sde_arm9 发表于 2010-10-18 09:04:25

代码:
        //resive data
        I2C->DATA = 0XFF;
        for(n=0;n<(count-1);n++){
        DrvI2C_Ctrl(0, 0, 1, 1);    //clr si       
        while( I2C->CON.SI == 0 );                           //poll si flag
        *(byte+n)= I2C->DATA;                                                                   //读取数据
        }
        DrvI2C_Ctrl(0, 0, 1, 0);    //clr si       
        while( I2C->CON.SI == 0 );                           //poll si flag
        *(byte+n)= I2C->DATA;                                                                   //读取数据

读里程数据,测试结果:
应该输出 09 81 00 00实际输出 01 00 00 06

machao 发表于 2010-10-18 12:14:11

http://cache.amobbs.com/bbs_upload782111/files_34/ourdev_590731EE0GIH.jpg
(原文件名:未标题-1 拷贝.jpg)

根据上面的M051描述的过程和你提供的代码,我给出一段完整连续读代码,请你参考。

1。不知道你的24CXX是多大的,你把送高位地址字节屏蔽了,应该是小容量的EEPROM
2。注意:address(开始地址)   以及address+count(结束地址)的值必须是在该芯片同一个页地址范围内,不的跨页



/*i2c读数据*/
void read_i2c_byte(uint8_t *byte,uint32_t address,uint8_t count)
{
   uint8_t n;
   uint16_t i;

//send i2c start
    DrvI2C_Ctrl(1, 0, 1, 0);   //set start    发送启动位
    while (I2C->CON.SI == 0); //poll si flag

//send writer command
    I2C->DATA = 0XA0;    //发送写命令
    DrvI2C_Ctrl(0, 0, 1, 1);      //clr si   ===>最好应该 set ack,表示24CXX应该回ACK=1
    while( I2C->CON.SI == 0 );    //poll si flag

//send address high
//    I2C->DATA = (address>>8)&0XFF;
//    DrvI2C_Ctrl(0, 0, 1, 1);    //clr si and set ack
//    while( I2C->CON.SI == 0 );    //poll si flag
   
//send address low
    I2C->DATA = (address)&0XFF;   //发送地址
    DrvI2C_Ctrl(0, 0, 1, 1);      //clr si and set ack
    while( I2C->CON.SI == 0 );    //poll si flag

//send start flag
   DrvI2C_Ctrl(1, 0, 1, 0);      //clr si and send start 发送启动位
   while( I2C->CON.SI == 0 );    //poll si flag

//send read command
   I2C->DATA = 0XA1;         //发送读命令
   DrvI2C_Ctrl(0, 0, 1, 1);    //clr si ===>最好应该 set ack,表示24CXX应该回ACK=1
   while( I2C->CON.SI == 0 );    //poll si flag

//resive data
   I2C->DATA = 0XFF;

   for(n=0;n<(count-1);n++){
      DrvI2C_Ctrl(0, 0, 1, 1);      //clr si===>这里肯定set ack,表示M051收到数据回ACK=1
      while( I2C->CON.SI == 0 );    //poll si flag
      *(byte+n)= I2C->DATA;         //读取数据
   }

   DrvI2C_Ctrl(0, 0, 1, 0);    //clr si
   while( I2C->CON.SI == 0 );    //poll si flag
   *(byte+n)= I2C->DATA;    //读取数据

//send i2c stop
DrvI2C_Ctrl(0, 1, 1, 0);    //clr si and set stop   发送停止位
DrvI2C_Close(I2C_PORT0);    //关闭I2C
}

sde_arm9 发表于 2010-10-18 12:55:38

谢谢马潮老师,您是对的,代码已验证是能正确读写多字节。


再次表示感谢!


//send read command
   I2C->DATA = 0XA1;         //发送读命令
   DrvI2C_Ctrl(0, 0, 1, 1);    //clr si ===>最好应该 set ack,表示24CXX应该回ACK=1
   while( I2C->CON.SI == 0 );    //poll si flag
--------------------
试了,这里设不设应答都能得到正确结果。

呵呵,确信您能提供我们需要的培训!

machao 发表于 2010-10-18 13:41:25

恭喜我通过你的考核。

下面还是要提醒的是关于是否使用“官方库”的问题。

你的代码肯定使用了官方的库,函数DrvI2C_Ctrl()就是这个库中的。下面是它的原形。

void DrvI2C_Ctrl(uint8_t start, uint8_t stop, uint8_t intFlag, uint8_t ack)
{       
        uint32_t Reg = 0;
               
        if (start)
                Reg |= I2C_STA;
        if (stop)
          Reg |= I2C_STO;
        if (intFlag)
                Reg |= I2C_SI;
        if (ack)
                Reg |= I2C_AA;

        *((__IO uint32_t *)&I2C->CON) = (*((__IO uint32_t *)&I2C->CON) & ~0x3C) | Reg;                 
}

其实就是一个对I2C->CON寄存器赋值,从这里可以看出使用库效率是如何的。

官方同时还提供了直接操作寄存器的例子,我也贴出代码,你可以比较代码的效率,选择采用什么方式来编写自己的代码

/*-----------------------------------------------------------------------------
Function:                Read_Page                                                                        
                                                                                                         
Parameter:                                                                                                                                                                                        
                        None                                    
Returns:                                                                                                
                       None                                                                                    
Description:                                                                                          
                       Read out pages have been written and verify them.                                    
*-----------------------------------------------------------------------------*/
void Read_Page(void)
{
    uint8_t ucPage, ucRead_Byte;

    // START
    I2CON |= STA;         
    I2CON |= SI;                                       //Clear SI
    while ((I2CON & SI) != SI);                        //Check SI set or not
    I2CON &= ((~STA)&(~SI));                           //STA needs to be cleared after START codition is generated

    if (I2STATUS != 0x08)                              //Check status value after every step
    {
      LED = ERROR_CODE;
      while (1);
    }

    //Control byte (SLA+W)
    I2DAT = EEPROM_SLA | EEPROM_WR;
    I2CON |= SI;
    while ((I2CON & SI) != SI);

    if (I2STATUS != 0x18)            
    {
      LED = ERROR_CODE;
      while(1);
    }

    //Address High (DATA)
    I2DAT = 0;
    I2CON |= SI;
    while ((I2CON & SI) != SI);

    if (I2STATUS != 0x28)            
    {
      LED = ERROR_CODE;
      while (1);
    }

    //Address Low (DATA)
    I2DAT = 0;
    I2CON |= SI;
    while ((I2CON & SI) != SI);
    if (I2STATUS != 0x28)            
    {
      LED = ERROR_CODE;
      while (1);
    }

    // Repeated START
    I2CON |= STA;         
    I2CON |= SI;                                       //Clear SI
    while ((I2CON & SI) != SI);                        //Check SI set or not
    I2CON &= ((~STA)&(~SI));                           //STA needs to be cleared after START codition is generated

    if (I2STATUS != 0x10)                              //Check status value after every step
    {
      LED = ERROR_CODE;
      while (1);
    }

    //Control byte (SLA+R)
    I2DAT = EEPROM_SLA | EEPROM_RD;
    I2CON |= SI;
    while ((I2CON & SI) != SI);

    if (I2STATUS != 0x40)            
    {
      LED = ERROR_CODE;
      while (1);
    }

    I2CON |= AA;

    for (ucPage = 0; ucPage <= 15; ucPage++)    //Read Microchip 24xx256 first 16 pages
    {
      //Data
      for (ucRead_Byte = 0; ucRead_Byte <= (PAGE_SIZE - 1); ucRead_Byte++)
      {
            I2CON |= SI;
            while ((I2CON & SI) != SI);
            if (I2STATUS != 0x50)            
            {
                LED = ERROR_CODE;
                while (1);
            }

   
            if (I2DAT != (ucPage))                     //Verify
            {
                LED = ERROR_CODE;
                while (1);
            }
      }
    }   

    //send a NACK to disconnect 24xx256
    I2CON &= (~AA);
    I2CON |= SI;
    while ((I2CON & SI) != SI);
      
    //STOP
    I2CON |= STO;
    I2CON |= SI;
    while (I2CON & STO);                             //To ensure STOP condition is generated, polling STO flag
}


实际上,不管采用哪种方式,都需要了解I2C的规程,了解M051的I2C接口是如何工作的,在这个方面是相同的。好象使用库可以少记几个具体位的名称和作用,但换得的代价是效率低,同时你也需要化时间去知道这个库函数的使用。更加可怕的是,如果库函数中有BUG,。。。。。

machao 发表于 2010-10-18 14:27:40

系统如果需要大批量的连续读/写操作I2C,高效率的方法是使用I2C的中断。

这样大量的 while( I2C->CON.SI == 0 );    //poll si flag时间都可以省掉。要知道硬件I2C的速度在怎么高,也比不上MCU的指令执行速度。

如果你使用UNC系列,那么效率最高的是I2C中断触发DMA传送,这样,在连续的一个批量写/读过程中,CPU的干涉处理降到最少。

sde_arm9 发表于 2010-10-19 22:08:20

对马老师的无私奉献精神表示钦佩!
受您的指点是我们的福分。

有些仪表有钟表功能,我们使用外部主晶振作为时钟源,通过定时器定时唤醒芯片来处理计时,看了半天M051手册,休眠时只有10K时钟能工作,实现不了钟表功能精度。看来只能使用nuc100的芯片了,这个有32k外部晶振。
仪表非工作状态电流要求:<2mA
有钟表的一般做到4mA

machao 发表于 2010-10-19 23:29:55

如果需要做真正的RTC,我建议还是采用专用的RTC芯片,精度好,耗电少,系统软件设计还剩了不少麻烦事情。一旦MCU跑非,时钟总是正确的。(有些系统当MCU跑飞后,可以通过DOG等措施拉回正常,尽管时间短,外观上没有影响系统工作,但实际时钟系统已经不正常了,至少需要重新设置正确的RTC时间)

专用RTC芯片也不贵,核算M051+RTC芯片与NUC100不会差多少,但RTC系统好多了,系统设计也简单很多。

带RTC功能的MCU做不到实际上的真正的RTC,你能保证MCU永远运行正常吗,系统不会掉电吗。我做过实验,断电后,用一个470u的电容供RTC专用芯片工作,可以保证48小时正常。

如果不是真正的RTC,而且系统经常处于断电不使用状态,那么10K的时钟也能做RTC。

拿一个手机,把电池拿掉,放上几小时,然后上电池打开,如果发现时钟不对了,需要设置,就是假的RTC。如果时钟正确,就是真的RTC。

你的仪表需要哪种?

sde_arm9 发表于 2010-10-20 09:17:22

仪表上只显示小时和分钟

仪表不会断电,但芯片干扰复位是有的。

10K时钟精度很差,+-50%

rtc芯片不错,但不知价格如何?



-----------------------------
这个应该可以
PCF8563,价格0.70元, i2c接口

点击此处下载 ourdev_591426WXBS0L.pdf(文件大小:232K) (原文件名:pcf8563.pdf)

machao 发表于 2010-10-20 10:35:14

回复【16楼】sde_arm9
仪表上只显示小时和分钟
仪表不会断电,但芯片干扰复位是有的。
10k时钟精度很差,+-50%
rtc芯片不错,但不知价格如何?
-----------------------------
这个应该可以
pcf8563,价格0.70元, i2c接口
点击此处下载
-----------------------------------------------------------------------

仪表不会断电,但芯片干扰复位是有的。===》那么你用MCU构成的RTC也就失效了。

既然仪表不会断电,那么串个二极管给RTC芯片供电,并上470u电容。如果万一仪表断电,至少RTC还能工作48小时。

一片RTC才0.7元(我的概念在1-2元,零售),系统软件相应也简单了,自己盘算。

再下去要付培训费了。这样的培训上哪找,价格很高的。

sde_arm9 发表于 2010-10-21 10:17:53

哈哈!
这样的培训真的没处找,付费付费

hedonghuxiao 发表于 2010-10-21 22:22:23

请问马老师,
为什么我在vista下面先装MDK4.12,然后装Nu-Link_Keil_Driver.exe,打开MDK,然后新建project,找不到新塘的M0系列,只能找到NUC系列呢???

dpjkflyq 发表于 2010-10-27 16:03:41

你们都犀利!我无语。

hotpower 发表于 2010-12-4 13:58:48

华邦的这个例程不怎么样,没一点i2c的风格,那个回调函数的例程还有些模样。

our2008 发表于 2013-3-20 10:01:22

和马老师的想法一样,坚持使用寄存器的路过

xuanfong1 发表于 2014-4-27 21:05:56

路过路过了顺便看看
页: [1]
查看完整版本: m051的i2c读写24cxx,如何连续读?