打印
[PIC®/AVR®/dsPIC®产品]

【Curiosity Nano测评报告】+I2C通讯的尝试

[复制链接]
1099|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hu9jj|  楼主 | 2020-6-25 18:04 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
    为了建立与DS1307日历芯片及EEPROM芯片的通讯,我尝试着开通I2C通讯,可是进入MMC之后,竟然找不到I2C的选项,没有其他办法,只好设置了RB1和RB2两个引脚,准备使用软件I2C。我将原来用在32位单片机的代码移植过来,但在设置SDA引脚IO方向时却遇到障碍,我开始使用pin_manager.h文件中的宏定义来设置SDA引脚的方向,结果不起作用:
#define SDA_SetDigitalInput()    do { TRISBbits.TRISB2 = 1; } while(0)
#define SDA_SetDigitalOutput()   do { TRISBbits.TRISB2 = 0; } while(0)

    然后又分析datasheet,参照pin_manager.c中的代码来控制SDA的IO方向,结果仍未达到目的,代码如下:
/***************************************************
函数功能:DAT引脚配置
入口参数:x
***************************************************/
void SI2C_PortConfig(uint8_t dir)              //DAT端口配置(根据需要配置成输出或输入模式)
{
    if(0 == dir){                                      //SDA输出模式
        LATB  = 0x20;                               //0010 0000     LATB = 0x20;  
        TRISB = 0xD9;                              //1101 1101     TRISB = 0xD9
//        ANSELB = 0xD9;                           //1101 1001
    }
    else{                                               //SDA输出模式
        LATB  = 0x24;                             //0010 0100
        TRISB = 0xDD;                            //1101 1101  IN
//        ANSELB = 0xDD;                           //1101 1101
    }
    ANSELB = 0xD9;  //1101 1001
    WPUB    = 0x00;
    ODCONB  = 0x00;
    SLRCONB = 0xFF;
    INLVLB  = 0xFF;
}


    另外在测试过程中还发现一个问题:在逐位发送数据的循环中,每一位花费的时间却不一样,一次比一次用时更短(详见下图),用了几天都没有查出原因。


    循环处理的代码如下:
/******************************************************************************************************************************************
* 函数名称: I2C_Send()
* 功能说明:        向IIC总线发送一个字节的数据
* 输    入: byte dat         要发送的数据
* 输    出: 无
******************************************************************************************************************************************/
void SI2C_Send(uint8_t dat)
{
        uint8_t i;
//        SDA_OUT;
//    SDA_SetDigitalOutput();
    SI2C_PortConfig(0);
        SCL_0;               //拉低时钟开始数据传输
        for(i=0;i<8;i++)
        {
//                if(((dat&0x80)>>7) == 1) SDA_1;//准备好SDA数据
        if((dat>>(7-i))&0x01) SDA_1;
                else SDA_0;
                dat>>=1;
//                delay_us(5);
        NOP();
                SCL_1;                         //拉高时钟等待从设备读取数据
//                delay_us(9);
        NOP();
        NOP();
        NOP();
        NOP();
        NOP();
        NOP();
                SCL_0;                         //拉低时钟准备下一位数据
//                delay_us(5);
//        NOP();
        }
}



使用特权

评论回复
沙发
hu9jj|  楼主 | 2020-6-26 15:55 | 只看该作者
    参考坛友Transformer的帖子才知道PIC18F47开发板在MCC中I2C和SPI的选择均在MSSP中,添加了MSSP之后,可以再选择SPI或I2C,如下图:

    为了以后的测试,我将MSSP1和MSSP2均添加进来了,其中MSSP1选作SPI1,MSSP2选作I2C2,并按照扩展板的总线结构选择好了相应的引脚:


    由于没有找到范例,只好分析i2c文件中的各个函数,尝试驱动I2C。从I2C的头文件中可以获知除了初始化函数void I2C2_Initialize(void)外,对外提供的公用回调函数仅有7个,分别是:
打开从设备         I2C2_Open(i2c2_address_t address)
关闭从设备         I2C2_Close(void)
确定读写操作      I2C2_MasterOperation(bool read)
设置写操作         I2C2_MasterWrite(void)
设置读操作         I2C2_MasterRead(void)
设置超时            I2C2_SetTimeout(uint8_t timeOut)
设置要使用的数据缓冲区和要传输的字节数  I2C2_SetBuffer(void *buffer, size_t bufferSize)

    其中第4、5个设置读写操作实际上是调用第3个函数,这样实际使用的只有5个函数,另外还有5个事件回调函数。经过这样梳理,驱动I2C相对就容易多了,目前正在测试代码,待驱动成功后再向各位汇报。

使用特权

评论回复
板凳
hu9jj|  楼主 | 2020-6-26 16:32 | 只看该作者
    按照初步的理解,我写了读写操作的代码,但测试却无法通过,程序停滞在下图的位置:


    我的代码如下,不知道是哪里有错误:
/******************************************************************************************
* 函数名称:        DS1307_read_date()
* 功能说明: 读取DS1307日期时间数据
* 输    入: 无
* 输    出: 无
******************************************************************************************/
void DS1307_read_date(void)
{
    I2C2_Open(DS1307_ADDR);                                //打开指定地址的从设备
    I2C2_SetBuffer(DS_Addr, 1);                            //指定操作地址
    I2C2_MasterOperation(false);                           //发送操作地址
    I2C2_SetBuffer(DS_Buff, 8);                            //指定数据缓存
    I2C2_MasterOperation(true);                            //选择读操作
    I2C2_Close();                                          //关闭从设备

    second = ((DS_Buff[0]&0x70)>>4)*10 + (DS_Buff[0]&0x0F);//秒,屏蔽秒的第7位的标志
        minute = ((DS_Buff[1]&0x70)>>4)*10 + (DS_Buff[1]&0x0F);//分(取低7位)
        hour = ((DS_Buff[2]&0x10)>>4)*10 + (DS_Buff[2]&0x0F);  //时(取低5位)
        week = (DS_Buff[3]&0x07);                              //周(取低3位)
    day = ((DS_Buff[4]&0x30)>>4)*10 + (DS_Buff[4]&0x0F);   //日(取低6位)
    month = ((DS_Buff[5]&0x10)>>4)*10 + (DS_Buff[5]&0x0F); //月(取低5位)
        year = 2000 + (DS_Buff[6]>>4)*10 + (DS_Buff[6]&0x0F);  //年

}


/******************************************************************************************
* 函数名称:        DS1307_write_date()
* 功能说明: 读取DS1307日期时间数据
* 输    入: 无
* 输    出: 无
******************************************************************************************/
void DS1307_write_date(void)
{
    uint8_t temp;
       
    DS_Buff[0] = 0;                      //秒
    temp = ((minute/10)<<4|(minute%10));
    DS_Buff[1] = temp;                   //分
    temp = ((hour/10)<<4|(hour%10));
    DS_Buff[2] = temp;                   //时

    DS_Buff[3] = week;                   //星期
    temp = ((day/10)<<4|(day%10));
    DS_Buff[4] = temp;                   //日
    temp = ((month/10)<<4|(month%10));
    DS_Buff[5] = temp;                   //月
    temp = ((year%100)/10<<4|(year%10));
    DS_Buff[6] = temp;                   //年
       
    I2C2_Open(DS1307_ADDR);                                //打开指定地址的从设备
    I2C2_SetBuffer(DS_Addr, 1);                            //指定操作地址
    I2C2_MasterOperation(false);                           //发送操作地址
    I2C2_SetBuffer(DS_Buff, 7);                            //指定数据缓存
    I2C2_MasterOperation(false);                           //写入数据
    I2C2_Close();                                          //关闭从设备
}


使用特权

评论回复
地板
GIGGWANG| | 2020-6-28 14:19 | 只看该作者
怎么感觉软硬IIC混在一起了

使用特权

评论回复
5
hu9jj|  楼主 | 2020-6-28 15:03 | 只看该作者
GIGGWANG 发表于 2020-6-28 14:19
怎么感觉软硬IIC混在一起了

1楼是软件I2C,因为SDA转换IO方向不会操作,所以没成功。2楼开始使用硬件I2C,没有找到示例,按自己理解写的读写函数,结果也没成功。

使用特权

评论回复
评论
GIGGWANG 2020-6-29 15:22 回复TA
你用的哪颗料,读的什么IIC设备,我有用过几个IIC的传感器,要不要参考下。 
6
hu9jj|  楼主 | 2020-7-7 17:08 | 只看该作者
经过一周多时间的反复调试,软件I2C驱动DS1307日历模块已经成功,参见:https://bbs.21ic.com/icview-2988712-1-1.html

使用特权

评论回复
7
snriycq| | 2020-7-19 19:14 | 只看该作者
本帖最后由 snriycq 于 2020-7-19 19:16 编辑

楼主硬件I2C测试成功了吗?

我用mcc编写I2C时,它给出了一个调用的样例,由于刚刚学习pic,基础比较差,没有理解清楚,发给楼主,看看:
static i2c1_operations_t rd1RegCompleteHandler(void *ptr);
static i2c1_operations_t rd2RegCompleteHandler(void *ptr);
static i2c1_operations_t wr1RegCompleteHandler(void *ptr);
static i2c1_operations_t wr2RegCompleteHandler(void *ptr);
static i2c1_operations_t rdBlkRegCompleteHandler(void *ptr);
我觉得楼主还是选从读取或写入1个字符进行测试比较好,
读取一个字符:
uint8_t I2C1_Read1ByteRegister(i2c1_address_t address, uint8_t reg)
{
    uint8_t returnValue = 0x00;

    while(!I2C1_Open(address)); // sit here until we get the bus..
    I2C1_SetDataCompleteCallback(rd1RegCompleteHandler,&returnValue);
    I2C1_SetBuffer(®,1);
    I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
    I2C1_MasterWrite();
    while(I2C1_BUSY == I2C1_Close()); // sit here until finished.

    return returnValue;
}


读取N个字符:

void I2C1_ReadNBytes(i2c1_address_t address, uint8_t *data, size_t len)
{
    while(!I2C1_Open(address)); // sit here until we get the bus..
    I2C1_SetBuffer(data,len);
    I2C1_MasterRead();
    while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}


查看了一下楼主的出错信息,是SSP2IF标志位未软件清零造成的,说实话,看过高显生的视频,也将其代码对着数据手册好好研究了一下,基本上他每调用一次芯片地址,或寄存器地址或数据,都要软件清零SSPIF,但在看MCC写的程序,实在太繁杂了,没有找到每次软件清零SSPIF,所以很是头疼,最主要的原因还是我的测试板上没有DS1307,无法自己测试,写给楼主,希望楼主能有所突破,我也学习了解一下,谢谢!
高显生的PIC18F4520调用DS1307的程序代码也一并贴出来:
unsigned char read_onechar(unsigned char chip_add, unsigned char reg_add)
{
        //PIF18F4520数据手册page195
        unsigned char TEMP;
        SSPIF=0;
        SEN=1;
        while(!SSPIF);
        SSPIF=0;
       
        SSPBUF=chip_add|0x00;        //写器件地址,R/W=0,下一步写入;
        while(!SSPIF);
        SSPIF=0;
       
        SSPBUF=reg_add;        //写DS1307寄存器地址;
        while(!SSPIF);
        SSPIF=0;
       
        NOP();
       
        RSEN=1;
        while(!SSPIF);
        SSPIF=0;
       
        SSPBUF=chip_add|0x01;        //写器件地址,R/W=1,下一步读出;
        while(!SSPIF);
        SSPIF=0;
       
        NOP();NOP();NOP();
       
        RCEN=1;
        while(!SSPIF);
        SSPIF=0;
       
        TEMP=SSPBUF;        //读收到的数据//////////////////
        //while(!SSPIF);
        //SSPIF=0;
       
        ACKDT=1;        //应答数据是高是低
        ACKEN=1;        //产生应答
        while(!SSPIF);
        SSPIF=0;
       
        PEN=1;
        while(!SSPIF);
        SSPIF=0;
       
        return TEMP;
}



说不清楚,把代码发给楼主,望楼主能研究一下,全部都是MCC生成的!



PIC18F27Q10_I2C.X.zip

55.83 KB

使用特权

评论回复
8
hu9jj|  楼主 | 2020-7-20 15:30 | 只看该作者
    十分感谢楼上的热心帮助,MCC的I2C示例我也找到了,但按照这个示例改写的I2C操作依旧会死循环,提供的高显生的代码也许是因为缺少相应的宏定义而不能编译:

    我再仔细对照一下MCC的设置,看看是否问题出在这里。

使用特权

评论回复
9
hu9jj|  楼主 | 2020-7-20 17:18 | 只看该作者
    对照7楼提供例子的配置,检查和修改了我的配置,添加了中断使能:


    相应的设置也修改成一致:


    然而代码测试时仍在下图的绿色行中无法通过:



使用特权

评论回复
10
hu9jj|  楼主 | 2020-7-20 17:57 | 只看该作者
    用逻辑分析检查时序,开机时报时序图如下,SDA有反映,SCL一直就是高电平:

    死循环时的时序图如下,两者都是直线:




使用特权

评论回复
11
antusheng| | 2020-7-20 23:24 | 只看该作者
来看看,好玩不

使用特权

评论回复
12
snriycq| | 2020-7-21 21:27 | 只看该作者
本帖最后由 snriycq 于 2020-7-21 21:29 编辑
hu9jj 发表于 2020-7-20 15:30
十分感谢楼上的热心帮助,MCC的I2C示例我也找到了,但按照这个示例改写的I2C操作依旧会死循环,提供的 ...

我查看了一下,你用的是PIC18F27Q10, 和PIC18F4520有所区别,PIC18F27Q10有2个I2C控制,而PIC18F4520只有一个,不过区别不大,只需再所属的寄存器名即可。

我已经帮你改好了,你测试一下,注意一下,你好像用的是第二组寄存器,因为我生成的是I2C1_XXXX, 而你生成的是I2C2_XXXX,如果是这样,你就将SSP1全部替换成SSP2,应该就可以了。
main.zip (563 Bytes)

研究了一下MCC生成的程序,是用 有限状态机 的方法写的,有点难,正在学习中。
看了一下你上传的调试图片,SCL始终为高电平,说明压根就没开始。因为开始后SCL会输出时钟。感觉开始条件没满足。




207745f16ea754fc63.png (417.65 KB )

207745f16ea754fc63.png

使用特权

评论回复
13
hu9jj|  楼主 | 2020-7-22 08:51 | 只看该作者
我想也许是没有配置好的问题,数据表上没有找到I2C使用的哪个定时器,我启用了定时器1和6,会不会是有冲突?

使用特权

评论回复
14
hu9jj|  楼主 | 2020-7-22 10:36 | 只看该作者
本帖最后由 hu9jj 于 2020-7-22 10:49 编辑

    将SSP1替换成SSP2之后编译出错:

    提示没有该成员:error: no member named 'RSSP2CON2bits' in 'SSP2CON2bits_t'。


    替换后的代码如下:
void write_onchar(unsigned char chip_add, unsigned char reg_add, unsigned char data)
{
        //PIF18F4520数据手册page194
        PIR3bits.SSP2IF=0;
        SSP2CON2bits.SEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        SSP2BUF=chip_add|0x00;        //R/W=0;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2BUF=reg_add;                //从器件寄存器地址
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2BUF=data;                //从器件寄存器数据
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2CON2bits.PEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
}


unsigned char read_onechar(unsigned char chip_add, unsigned char reg_add)
{
        //PIF18F4520数据手册page195
        unsigned char TEMP;
        PIR3bits.SSP2IF=0;
        SSP2CON2bits.SEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2BUF=chip_add|0x00;        //写器件地址,R/W=0,下一步写入;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2BUF=reg_add;                //写DS1307寄存器地址;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        NOP();
        
        SSP2CON2bits.RSSP2CON2bits.SEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2BUF=chip_add|0x01;        //写器件地址,R/W=1,下一步读出;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        NOP();NOP();NOP();
        
        SSP2CON2bits.RCEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        TEMP=SSP2BUF;                //读收到的数据//////////////////
        //while(!PIR3bits.SSP2IF);
        //PIR3bits.SSP2IF=0;
        
        SSP2CON2bits.ACKDT=1;        //应答数据是高是低
        SSP2CON2bits.ACKEN=1;        //产生应答
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        SSP2CON2bits.PEN=1;
        while(!PIR3bits.SSP2IF);
        PIR3bits.SSP2IF=0;
        
        return TEMP;
}



使用特权

评论回复
15
snriycq| | 2020-7-22 12:11 | 只看该作者
本帖最后由 snriycq 于 2020-7-22 12:13 编辑
hu9jj 发表于 2020-7-22 10:36
将SSP1替换成SSP2之后编译出错:

    提示没有该成员:error: no member named 'RSSP2CON2bits' in 'S ...


不好意思,替换时替换多了,把红色的删掉再试试!


966155f17bc377a3cd.png (17.92 KB )

966155f17bc377a3cd.png

使用特权

评论回复
16
hu9jj|  楼主 | 2020-7-22 16:03 | 只看该作者
snriycq 发表于 2020-7-22 12:11
不好意思,替换时替换多了,把红色的删掉再试试!

编译顺利通过了,但测试时仍在下面绿色行中死循环,我想可能还是定时器没有设定好的原因。

使用特权

评论回复
17
snriycq| | 2020-7-22 21:13 | 只看该作者
hu9jj 发表于 2020-7-22 16:03
编译顺利通过了,但测试时仍在下面绿色行中死循环,我想可能还是定时器没有设定好的原因。

...


可能是时钟频率吧,高显生的视频是设定为09H的,但MCC设定的是03H,但查表又找不到这个值,你试试改一下这个值。选时钟频率在4MHz,SSP1ADD = 0x09;


使用特权

评论回复
18
hu9jj|  楼主 | 2020-7-23 08:32 | 只看该作者
snriycq 发表于 2020-7-22 21:13
可能是时钟频率吧,高显生的视频是设定为09H的,但MCC设定的是03H,但查表又找不到这个值,你试试改一下 ...

    非常感谢您的热心帮助,不过问题仍未得到解决,用逻辑分析看不到SCL的时序变化,程序仍然进入死循环。相关的MCC设置我已经反复核对过,这应该不是驱动DS1307的问题,而是硬件I2C可能没有启动的问题,不过我现在用软件I2C驱动DS1307很正常,硬件I2C的问题留待以后再说吧。
    下图为重启时抓取的时序图,没有看到SCL的任何变化,进入死循环后SCL一直是高电平,SDA一直是低电平。

使用特权

评论回复
19
hu9jj|  楼主 | 2020-7-23 08:46 | 只看该作者
改成SSPADD=0x09后会影响到软件I2C的正常操作,只有改回0x03后程序才恢复正常。

使用特权

评论回复
20
snriycq| | 2020-7-24 19:46 | 只看该作者

我用proteus搭建了一个简单的环境,测试没有问题,我选用的是4MHz的晶振,设定的波特值为100KHz。

使用特权

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

本版积分规则

认证:Microchip
简介:让我们来为您提供帮助。我们可提供各种资源来帮助您解决一切问题。是否需要与我们的客户支持团队联系?您可以通过电话、在线聊天功能或电子邮件与他们联系。

144

主题

1031

帖子

11

粉丝