历史上的今天
返回首页

历史上的今天

今天是:2025年04月25日(星期五)

正在发生

2020年04月25日 | PIC单片机之I2C通信(主模式)

2020-04-25 来源:eefocus

    我们今天来讲I2C通信。那I2C通信的特点是什么能。我们一般使用的串口 (半双工异步串行通信)与I2C 有什么区别呢。


    串口(半双工异步串行通信):就是好像朋友在对话。我可以主动和你讲话,你也可以主动和我讲话。


    I2C:就好像上下级对话。一个领导面对一个或者多个员工。只有领导主动说话的份儿,下面的员工不能主动说话。只有领导问了,员工才能答。


    I2C通信


    I2C通信只需要两个引脚 一个数据线,一个时钟线。 数据线顾名思义就是用来传递数据的。时钟线是来决定数据传输的速度。当时钟线为高电平时,数据线上的数据才会被认为是有效的。


    数据线的 数据有四种状态 : 高电平,低电平,下降沿(高电平变低电平),上升沿(低电平变高电平)。


    当时钟线为高电平时候这四种状态分别代表:1,0,起始位,停止位。

    

    如果我们发送的数据为十六进制的0x88即是二进制为10001000的数据是怎么发送的呢?我们就以此为例一步步讲解。

    1、常态


    在不发送任何数据的时候数据线和时钟线都为高电平。一般为了保证能带动128个从设备。大部分芯片的I2C通信接口都是用开漏输出(只能接通地不能输出高电平),所以数据线和时钟线都要加上拉电阻。


    2、起始


    当开始发送数据的时候 时钟线为高同时数据线从 高电平变低电平,代表开始发送数据。


    3、发送数据


    发送完起始位后 时钟线变为低电平,在发送每一位的数据之前时钟线有一段低电平,主要的作用是给数据线做电平变化用的。 


    我们现在要发送的第一个位是 1。


    1、时钟线为低,同时数据线从低电平变成高电平。


    2、接着时钟线变为高电平,此时接收方得知时钟线为高,便查看数据线为高电平 说明数据为 “1”。


    3、我们要发送的下一个位为0。时钟线再变为低,同时数据线从高电平变成低电平。


    4、接着时钟线再变为高电平,此时接收方得知时钟线为高,便查看数据线为低电平 说明数据为"0"。


    5、再下一个为还为0。时钟线再变为低,同时数据线一直保持低电平不变。


    6、接着时钟线再变为高电平,此时接收方得知时钟线为高,便查看数据线为低电平 说明数据为”0“。


    以此类推 直到发送完所有的位。


    4,应答(ACK)


    当接收方接收完一个字节的数据就要告诉对方我收到了。接收方如果接收到数据则控制数据线输出低电平。否则为高电平。


    5,停止


     没有下一个字节要发送,最后时钟线变为高电平后,数据线从低电平变为高电平。代表数据发送停止。


    实例讲解: 使用单片机使用 RSM2257 电子音量控制芯片来控制音量。一个按键按下,声音变大,一个按键按下,声音变小。在加上一个按键,控制一个LED亮灭的程序。而且音量掉电保存。


    介绍RSM2257.


    子地址


    在I2C通信中每一个从设备都有个子地址,因为I2C支持一主多从,也就是说有一个主机可以连接多个从机。每个从机,都有个地址。就好像每个人的名字一样来区分不同的设备。下面是RSM2257接口协议,首先先发送RSM2257 设备地址 10001000.然后再发送数据。

    数据

    

    RSM2257的数据是用来表示音量大小的。我们控制两个音频通道,以10dB为单位降低或增加音量。从功能设置位表格中可知数据为 11100B2B1B0.


    B2B1B0的数值决定了音量。请详见 衰减设置位。


    单片机I2C通信初始化设置


   1、设置端口为输入


    TRISC0 = input;

     TRISC1 = input;

    

    2、设置模式 


    我们设置ssp1控制寄存器的 SSP1M<3:0>.我们需要的是I2C主模式。设置如下


           SSP1CON1bits.SSPM0 = 0;

           SSP1CON1bits.SSPM1 = 0;

           SSP1CON1bits.SSPM2 = 0;

           SSP1CON1bits.SSPM3 = 1;// I2C Master mode ,clock=Fosc/(4*(SSPxADD+1))


    3、设置时钟线频率RSM2257最大为100KHZ,我选择设置为50KHZ.


    使用计算公式clock=Fosc/(4*(SSPxADD+1)) 计算出SSP1ADD的值为0x9F;


    SSP1ADD=0x9F;


    4、开启I2C通信


    SSP1CON1bits.SSPEN = 1;


    单片机I2C发送程序


    1、发送起始位


    SSP1CON2bits.SEN = 1;//Start condition

    while(SSP1CON2bits.SEN == 1);//waiting for Start condition completed.


    2、发送地址

    PIR1bits.SSP1IF = 0;

    SSP1BUF = 0x88;//Device Address

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;

    // ~ACK  我们不理会接收方有没有应答。

    

    3、发送 10db音量控制的数据

    SSP1BUF = tx_data;//Data 10db level

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;


    4、发送1db音量控制的数据

   //  ~ACK

    SSP1BUF = 0xD0;//Data 1db level

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;


    5、发送停止位

   //  ~ACK

    SSP1CON2bits.PEN = 1;//Stop condition


    关于I2C通信协议,RSM2257,PIC MSSP 模块设置成I2C,更详细的内容就必须去看数据手册了。


    实例程序:程序分为main.c 和 define.h两个文件 芯片PIC16LF1823,开发环境MPLAB X IDE.


define.h文件


/**********RA*********/

//B'1111,1000'H F8

#define LED_SW  RA5//IN

#define UP_SW   RA4//IN

#define DOWN_SW RA3//IN

#define LED     RA2//OUT

//RA1

//RA0

/**********RC***********/

//H FF

//RC0   SCL

//RC1   SDA

#define input 1

#define LED_VALUE  1

#define UP_VALUE   2

#define DOWN_VALUE 3

#define key_delay  300


 main.c文件


#include

#include"define.h"

__CONFIG(FOSC_INTOSC&WDTE_OFF&PWRTE_ON&MCLRE_OFF&CP_ON&CPD_OFF&BOREN_ON


                                                          &CLKOUTEN_OFF&IESO_ON&FCMEN_ON);

__CONFIG(PLLEN_OFF&LVP_OFF) ;

void tx_pro(unsigned char tx_db);

unsigned char DB_VALUE;

void init_fosc(void)

{

 OSCCON = 0xF0;//32MHZ

}

void init_gpio(void)

{

 PORTA=0;

 LATA =0;

 ANSELA=0x00;

 TRISA =0xF8;



 PORTC=0;

 LATC=0;

 ANSELC = 0x00;

 TRISC =0xFF;

}

void init_i2c_master()

{

    TRISC0 = input;

    TRISC1 = input;

    SSP1CON1bits.SSPM0 = 0;

    SSP1CON1bits.SSPM1 = 0;

    SSP1CON1bits.SSPM2 = 0;

    SSP1CON1bits.SSPM3 = 1;// I2C Master mode ,clock=Fosc/(4*(SSPxADD+1))

    SSP1STATbits.SMP = 1;

    SSP1ADD = 0x9F;//SCL CLOCK Frequency 50KHZ

    SSP1CON1bits.SSPEN = 1;

}

void i2c_master_tx(unsigned char tx_data)

{

    SSP1CON2bits.SEN = 1;//Start condition

    while(SSP1CON2bits.SEN == 1);//waiting for Start condition completed.



    PIR1bits.SSP1IF = 0;

    SSP1BUF = 0x88;//Device Address

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;

    // ~ACK

    

    SSP1BUF = tx_data;//Data 10db level

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;

    

   //  ~ACK

    SSP1BUF = 0xD0;//Data 1db level

    while(PIR1bits.SSP1IF == 0);

    PIR1bits.SSP1IF = 0;

   //  ~ACK

    SSP1CON2bits.PEN = 1;//Stop condition

}

void delay(unsigned int n)

{

    while(n--);

}

unsigned char key_board(void)

{

    if(LED_SW==1)

    {

        delay(key_delay);

        if(LED_SW==1)

        {

            while(LED_SW==1);

            return LED_VALUE;

        }

    }

    if(UP_SW==1)

    {

        delay(key_delay);

        if(UP_SW==1)

        {

            while(UP_SW==1);

            return UP_VALUE;

        }

    }

    if(DOWN_SW==1)

    {

        delay(key_delay);

         if(DOWN_SW==1)

        {

            while(DOWN_SW==1);

            return DOWN_VALUE;

        }



    }

    return 0;



}

void DB_INC(void)

{

    if(DB_VALUE < 7)

    {

        DB_VALUE++;

        eeprom_write(0x00, DB_VALUE);//将音量值保存到EEPROM这样掉电后数据也不会丢失。

        tx_pro(DB_VALUE);

    }


}

void DB_DEC(void)

{

    if(DB_VALUE  > 0)

    {

        DB_VALUE --;

        eeprom_write(0x00, DB_VALUE);

        tx_pro(DB_VALUE);

    }

}

void tx_pro(unsigned char tx_db)

{

    tx_db |= 0xE0;           //将高三位设置为1。表示两个音频通道,以10dB为单位降低或增加音量

    i2c_master_tx(tx_db);//I2C发送数据程序

}

/*

 * 

 */

int main(int argc, char** argv) {



    unsigned char keyvalue;

    init_fosc();

    init_gpio();

    init_i2c_master();

    LED=0;

    DB_VALUE= eeprom_read(0x00);//读eeprom 中保存的音量值

    if(DB_VALUE > 7)//如果之前没有设置过则音量不衰减

    {

      DB_VALUE = 0;

    }

    tx_pro(DB_VALUE);//用I2C通信设置RSM2257的音量

    while(1)

    {

         keyvalue=key_board();//判断按键程序,

         switch(keyvalue) 

         {

             case LED_VALUE://LED按键按下

             {

                 LED = ~LED;

             };break;

             case UP_VALUE://音量加

             {

                 DB_INC();

                 

             };break;

             case DOWN_VALUE://音量减

             {

                 DB_DEC();

                 

             };break;

         }



    }



}

推荐阅读

史海拾趣

Electro-Films Inc Semi-Films Div公司的发展小趣事

随着市场的扩大和竞争的加剧,Semi-Films Division 意识到品质是品牌的核心。因此,公司加大了对产品质量控制的投入,引入了先进的生产设备和检测手段,确保每一批产品都符合客户的要求。同时,公司还建立了完善的质量管理体系,对生产过程进行全程监控,确保产品质量的稳定性和可靠性。这些努力使得 Semi-Films 的产品在市场上赢得了良好的口碑。

DELTA公司的发展小趣事

Delta公司始终将技术创新作为推动企业发展的核心动力。公司不断投入巨资进行技术研发和创新,积极引进先进技术和设备,培养高素质的研发人才。这些努力使Delta在电源管理、能源管理、智能绿生活等领域取得了多项技术突破和专利成果,不仅提升了公司的核心竞争力,还引领了整个行业的发展方向。

Frequency Electronics Inc公司的发展小趣事

高频电子深知技术创新是企业持续发展的核心动力。因此,公司一直保持着对研发的持续投入,不断推出新产品、新技术和新服务。同时,高频电子还积极与高校、科研机构等合作,共同开展前沿技术的研发和应用。这些努力使得高频电子在技术创新方面始终保持着领先地位,为公司的长期发展奠定了坚实的基础。

综上所述,高频电子通过战略转型、技术创新、全球化布局、多元化业务布局以及持续投入研发等举措,实现了从一家小型国防合同制造商到全球领先的高科技供应商的华丽蜕变。这些故事不仅展现了高频电子的发展历程和成就,也为我们揭示了企业成功背后的关键因素。

DRI Relays Inc公司的发展小趣事

为了满足全球客户的需求,DRI Relays Inc公司开始实施全球化战略。公司先后在亚洲、欧洲和美洲等地设立了分公司和研发中心,以便更好地了解当地市场需求和提供定制化服务。同时,公司还积极参与国际展览和交流活动,加强与全球客户的沟通和合作。这些举措不仅提高了公司的国际知名度,还为公司赢得了更多的订单和市场份额。

E. Dold & Söhne KG公司的发展小趣事

随着公司实力的不断增强,E. Dold & Söhne KG公司开始积极拓展海外市场。公司先后在多个国家和地区设立了分支机构或办事处,与当地企业开展合作,共同开发市场。此外,公司还积极参与国际展会和交流活动,与国际同行交流技术和管理经验,不断提升自身的竞争力。

CTC [Compact Technology Corp.]公司的发展小趣事

CTC公司深知人才是企业发展的核心。因此,公司一直注重人才培养和引进。通过与高校和研究机构的合作,CTC公司吸引了大批优秀的科研人才加入。同时,公司还建立了完善的培训体系,为员工提供持续的技能提升和职业发展机会。这些人才成为了CTC公司发展的中坚力量,为公司构筑了核心竞争力。

问答坊 | AI 解惑

AD6智能PDF导出时,中文字符无显示

Altium Designer 6 中,制件的原理图文件中若有中文字,用自带的\"智能PDF\"导出时,则在产生的PDF中显示不了中文字符.本人用Altium Designer 6.3~Altium Designer 6.9都试过,均有此问题,不知能否解决,请哪位大侠指点,若不能人为修改,是否要等待A ...…

查看全部问答>

6个变态的C语言Hello World程序

下面的六个程序片段主要完成这些事情: 输出Hello, World混乱C语言的源代码下面的所有程序都可以在GCC下编译通过,只有最后一个需要动用C++的编译器g++才能编程通过。 hello1.c 01     #define _________ }   02 ...…

查看全部问答>

谈谈天线锡焊要点

锡焊要点可以简单归纳如下:   1.使用烙铁(包括火烙铁)前,必须使烙铁头表面蒙上一层锡(俗称“吃锡”),这样烙铁头才能拉住锡。上锡的方法是在烙铁未加热前,将烙铁头挫光,除去氧化物或污垢,然后对刮亮后的烙铁头加热,直到光亮部分变成紫 ...…

查看全部问答>

请教Modbus高手makesoft:实现Modbus协议一定需要超时检测吗?

首先声明,我对Modbus不熟悉,尤其是如何实现它,最近才从网上下载了协议研究了一下,特此向高手请教。搞清楚这些问题,才能有效地在芯片中实现相应的功能,满足大家的需要。此帖的目的是继续另一帖的讨论:建议STM32的芯片加上串口超时功能, ...…

查看全部问答>

向高手请教一个关于STM32的PF,PG口的问题

                                 在STM32的DS里面,管脚介绍的表格中Main function(after reset)一栏中,大部分的IO口都是如PA7样式的口 ...…

查看全部问答>

关于正弦余弦函数的问题

我用CCS编程调试时,出现了“error: identifier \\"sinwt\\" is undefined”的错误信息,请请教一下是怎么回事?是不是需要加入某个头文件?还是我的写法有问题啊?谢谢大家~~~…

查看全部问答>

关于CCS在软件延时上的探讨

      在使用CCS的过程中,我发现在有关于有软件延时的程序中会出现问题,感觉延时没起到作用,比如一个简单的例子,让灯闪烁,使用delay函数(函数体内用两个for循环嵌套那种),这种延时就不会有效果,而用i=50000;   while ...…

查看全部问答>

STM32F407学习板全部例程+原理图

全部代码都已测试通过,若发现有什么问题请和我联系。因为时间仓促,基本上都是一些硬件测试程序。记得要把例程和固件库放在同一目录下,全部工程共用一个库的。至于SD卡里的文件只是几个测试图片和音 ...…

查看全部问答>

Kernel - Common Problems Booting Linux中文手册改1

Kernel - Common Problems Booting Linux中文手册改1…

查看全部问答>

EK-LM4F120XL ADC参考电压问题

在EK-LM4F120XL Launchpad中, 我分别用 AIN0(PE3口)采样1.3V电压 AIN1(PE2口)采样0.65V电压, AIN8(PE5口)采样1.95V电压, AIN9(PE4口)采样2.6V电压, 假如以3.0V作为参考电压,那么在数据寄存器中采样结果应该是: 1775,对应1.3V电压 ...…

查看全部问答>