PIC单片机之I2C通信-主-从模式

发布者:创意旋律最新更新时间:2016-11-04 来源: eefocus关键字:PIC单片机  I2C通信  主-从模式 手机看文章 扫描二维码
随时随地手机看文章
主模式:

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

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

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

    I2C通信

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

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

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

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

     

    1,常态

       在不发送任何数据的时候数据线和时钟线都为高电平。所以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;
         }
    }
}

从模式:

网上有许多讲解单片机 实现I2C主模式,但是从模式的很少。我现在就来讲讲PIC单片机使用MSSP模块实现I2C从模式。

    有关I2C协议的具体介绍可以看 《PIC单片机之I2C(主模式)》,我们这里直接讲解实例

    实例讲解:我们模仿 AT24C02 EEPROM 的协议。让一个主模式的单片机,来读取从模式单片机的数据。 

      下面为AT24C02的随机地址读取的协议。

           第一个字节 :输入7位地址和一位的写状态位,

           第二个字节:然后写入EEPROM数据地址,

           第三个字节:输入7位地址和一位的读状态位,

           第四~N个字节:读出的EEPROM的数据。

     

       我们来讲解下程序的基本思路:我们使能了MSSP中断,即是I2C接收中断,当PIC单片机接收到一个数据后就会产生中断。那是接收到设备地址,还是接收到数据,由SSP1STAT寄存器的状态位来判断。

需要判断的状态位分别是 :

       数据和地址:  用来判断接收到是地址还是数据

       启动位:         用来判断是否接收到启动位

       读写:             用来判断是写状态还是读状态。

       缓存满:        用来判断缓冲区是否满

    我们以随机地址读取为例:讲讲程序执行的过程

     1,从单片机接收到启示位和设备地址中断:我们判断SSP1STAT的状态位为(写状态,地址,缓存满,接收到启示位) 然后读取缓存中的设备地址, 接着在读取 需要读/写的数据地址。

     2,单片机再次接收到设备地址:我们判断是SSP1STAT的状态为(读状态)然后从设备就输出数据

   我们以写字节数据为例:

1,从单片机接收到启示位和设备地址中断:我们判断SSP1STAT的状态位为(写状态,地址,缓存满,接收到启示位) 然后读取缓存中的设备地址, 接着在读取 需要读/写的数据地址。

  2,单片机判断SSP1STAT的状态位为(写状态,数据,缓存满)那么单片机就接收输入的数据。

     初始化设置:

     1,设置I2C通信的两引脚为CLK SCL为输入,

          TRISB6 = input;
          TRISB4 = input;

     2,将MSSP设置为I2C从模式,七位从地址

           SSP1CONbits.SSPM0 = 0;
           SSP1CONbits.SSPM1 = 1;
           SSP1CONbits.SSPM2 = 1;
           SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address

      3,使能CLK时钟

        SSP1CONbits.CKP = 1; // enable clock

      4,设置从设备地址为 0xA0

      SSP1ADD =0xA0;       //slave address is 0xa0

      5,开启I2C

      SSP1CONbits.SSPEN=1;//enable I2c

      6,清楚状态标志
      SSPSTAT=0;
     7,使能I2C中断
     PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
     INTCONbits.PEIE = 1;
     INTCONbits.GIE =  1;

      

    如果你要使用PIC单片机I2C从模式只要使用下面的代码:

  将void i2c_salve_interrupt_tx();void i2c_salve_interrupt_rx();放到中断程序中,如下:

void interrupt isr(void)
{
      if(SSP1IE && SSP1IF)
   {
        i2c_salve_interrupt_tx();
        i2c_salve_interrupt_rx();
        SSP1IF=0;
   }

}

 将初始化函数init_i2c_slave();放到主函数中

void main()

{

  init_i2c_slave();

}

 

头文件 :i2c_salve.h

#ifndef _I2C_SALVE_H
#define _I2C_SALVE_H
void init_i2c_slave();
void i2c_salve_interrupt_tx();
void i2c_salve_interrupt_rx();
#endif

代码:i2c_salve.c

   #include;
#define input  1

#define  RX_BUF_LEN  29

#define  while_delay 6000

unsigned char i2c_address,word_address,Register[29];
unsigned  char RANDOM_READ,i2c_counter;
extern unsigned char A_readflag;
/*I2C SALVE */
void init_i2c_slave()
{
    TRISB6 = input;
    TRISB4 = input;
    SSP1CONbits.SSPM0 = 0;
    SSP1CONbits.SSPM1 = 1;
    SSP1CONbits.SSPM2 = 1;
    SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address
    SSP1CONbits.CKP = 1; // enable clock
    SSP1ADD =0xA0;       //slave address is 0xa0

    SSP1CONbits.SSPEN=1;//enable I2c
    SSPSTAT=0;


    PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
    INTCONbits.PEIE = 1;
    INTCONbits.GIE =  1;


}
/*I2C salve mode interrupt */
void i2c_salve_interrupt_tx()//master read
{
    unsigned char Temp;
    unsigned int  timercounter;


    Temp=SSP1STAT;
    Temp &= 0x2D;
    if(SSP1STATbits.R_nW ==1)//Read operation.
    {
                A_readflag=0;
              SSP1IF = 0;
              i2c_address =  SSP1BUF;
              i2c_counter = word_address;
             while(i2c_counter < RX_BUF_LEN)
             {
               SSP1BUF=Register[i2c_counter];//send data
               SSP1CONbits.CKP=1;// enable colck
               timercounter=while_delay;
             while(PIR1bits.SSP1IF == 0)
               {
                 timercounter--;
                 if(timercounter==0)
                 {
                     return;
                 }
                }//waiting for ~ACK
               SSP1IF = 0;
             if(SSP1CON2bits.ACKSTAT == 1)
              {
                return ; //NOACK
               }
               else
              {
              i2c_counter++;//ACK
            
               }
             }
            SSP1IF = 0;
    }
 }
 


void i2c_salve_interrupt_rx()//master writer
{
    unsigned char rx_status;
    unsigned char Temp;
    unsigned int  timercounter;
    rx_status=false;
    Temp=SSP1STAT;
    Temp &= 0x2D;
    if(Temp==0x09)//Write operation,last byte was an address,buffer is full
    {


         SSP1IF = 0;
         i2c_address =  SSP1BUF;
         timercounter=while_delay;
          while(PIR1bits.SSP1IF == 0)
          {
              timercounter--;
              if(timercounter==0)
               {
                     return ;
               }


          }//waiting for send ~ACK
          SSP1IF = 0;
          word_address = SSP1BUF;
          return ;
    }
    if(Temp==0x29)//Write operation,last byte was data,buffer is full
    {
        
        SSP1IF=0;
        Register[word_address]=SSP1BUF;
        word_address++;
        if(word_address>=RX_BUF_LEN)
            {
                word_address=0;
            }
    }


}

关键字:PIC单片机  I2C通信  主-从模式 引用地址:PIC单片机之I2C通信-主-从模式

上一篇:PIC16F877A的timer
下一篇:基于PIC32的RFID防盗系统设计

推荐阅读最新更新时间:2024-03-16 15:19

PIC单片机led灯左移右移
程序介绍:每隔100ms,移动一个灯亮 //** Date: Wednesday, November 21, 2012 21:26:32 #define MX_PIC //Defines for microcontroller #define P16F690 #define MX_EE #define MX_EE_SIZE 256 #define MX_SPI #define MX_SPI_BCB #define MX_SPI_SDI 4 #define MX_SPI_SDO 7 #define MX_SPI_SCK 6 #define MX_UART #define MX_UART_B #define
[单片机]
<font color='red'>PIC单片机</font>led灯左移右移
PIC单片机人机接口模块4×4行列式键盘的程序设计
    程序的主流程如图1所示。   图1 程序的主流程   程序主要分为两个部分:一个部分不停地监测是否有按键按下,另一个部分查看哪一个键按下。   在初始状态下,4个列输出端口输出低电平,即RD0~RD3输出低电平,然后持续监测4个行输入端口RD4~RD7的状态是不是高电平。   如果没有按键按下,则RD4~RD7的状态是高电平;如果有按键按下,则被按下的键对应的行输入端口的电平就会被拉低,RD4~RD7会有低电平出现,对4个行输入端口RD4~RD7的电平的监测即为对按键的监测。   在4个行输入端口RD4~RD7上出现低电平时,就转到查询程序SEE。 键盘 扫描子程序流程如图2所示,按键查询子程序流程如图
[嵌入式]
PIC单片机的优点
一、引言 据统计,我国的单片机年容量已达1-3亿片,且每年以大约16%的速度增长,但相对于世界市场我国的占有率还不到1%。这说明单片机应用在我国才刚刚起步,有着广阔的前景。培养单片机应用人才,特别是在工程技术人员中普及单片机知识有着重要的现实意义。 当今单片机厂商琳琅满目,产品性能各异。针对具体情况,我们应选何种型号呢?首先,我们来弄清两个概念:集中指令集(CISC)和精简指令集(RISC)。采用CISC结构的单片机数据线和指令线分时复用,即所谓冯.诺伊曼结构。它的指令丰富,功能较强,但取指令和取数据不能同时进行,速度受限,价格亦高。采用RISC结构的单片机数据线和指令线分离,即所谓哈佛结构。这使得取指令和取数据可同时进行,且
[单片机]
PIC单片机的类型以及特征介绍
对于pic单片机,大家或多或少均有所耳闻。但是,大家对pic单片机有多少了解呢?是否熟知不同类型的pic单片机呢? 由美国Microchip公司推出的PIC单片机系列产品,首先采用了RISC结构的嵌入式微控制器,其高速度、低电压、低功耗、大电流LCD驱动能力和低价位OTP技术等都体现出单片机产业的新趋势。现在PIC系列单片机在世界单片机市场的份额排名中已逐年升位,尤其在8位单片机市场,据称已从1990年的第20位上升到目前的第二位。PIC单片机从覆盖市场出发,已有三种(又称三层次)系列多种型号的产品问世,所以在全球都可以看到PIC单片机从电脑的外设、家电控制、电讯通信、智能仪器、汽车电子到金融电子各个领域的广泛应用。现今的PI
[单片机]
<font color='red'>PIC单片机</font>的类型以及特征介绍
PIC单片机入门总结
1. 安装软件流程 1)安装mplab8.85 2)安装CCS_PCWHD_4.120 a) 双击Compiler目录下的pcwhdupd.exe安装编译器,按照提示安装了编译器之后在桌面上会有PIC C Compiler,这个图标,这就是C编译器。 b)把PIC C profiler挂到mplab上。方法:双击plugins目录下的setup_mplab_plugin.exe按照提示完全安装即可。 2. 建立工程方法(pic wizard) 3. 简单的一个io口控制源码 注意调试模式和下载程序模式 1) 对配置位要进行正确配置 例如: #device HIGH_
[单片机]
<font color='red'>PIC单片机</font>入门总结
PIC单片机之SPI机框架
#include pic.h #include string.h #include STDIO.H __CONFIG(0x3F32); //芯片配置字 选择HS模式振荡器,关WDT typedef unsigned char uchar; typedef unsigned int uint; uchar resive=0; uchar resive1=0; uchar send_buf ={'S',0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xc,0xD,0xe,0xf}; uchar send_num=0; uchar resive_flag=0; #defi
[单片机]
PIC单片机是什么?
什么是PIC单片机? PIC单片机(Peripheral Interface Controller)是一种用来开发的去控制外围设备的集成电路(IC)。一种具有分散作用(多任务)功能的CPU。与人类相比,大脑就是CPU,PIC 共享的部分相当于人的神经系统。 PIC 单片机是一个小的计算机 PIC单片机有计算功能和记忆内存像CPU并由软件控制允行。然而,处理能力—存储器容量却很有限,这取决于PIC的类型。但是它们的最高操作频率大约都在20MHz左右,存储器容量用做写程序的大约1K—4K字节。 时钟频率与扫描程序的时间和执行程序指令的时间有关系。但不能仅以时钟频率来判断程序处理能力,它还随处理装置的体系结构改变(1*)。如果是
[单片机]
MAX517与单片机的I2C总线数据通信
摘要:介绍了I2C总线的特点及数据通信的基本协议,并以AT89C51单片机与美国MAXIM公司的8位电压输出DAC数模转换器MAX517之间的通信为例,详细介绍了通过I2C总线进行数据通信的具体硬件电路连接和其通信子程序的编程方法。 关键词:I2C总线;AT89C51;MAX517;数据通信 1 I2C总线的特点及基本通信协议 I2C总线是Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线串行数据线和串行时钟线即可使连接于总线上的器件之间实现信息传送,同时可通过对器件进行软件寻址,而不是对硬件进行片选寻址的方式来节约通信线数目,从而减少了硬件所占空间。因为总线已集成在片内,所以大大缩短了设计时间,
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved