js200300953 发表于 2012-6-24 15:42:29

基于中断的I2C库

本帖最后由 js200300953 于 2012-6-24 15:50 编辑

这个库是NUC140的,应该很容易就可以移植到其他芯片上(其实这个库是从AVRi移植过来的),特别是同为M3的STM32。
希望对大家有帮助。
废话少说,上代码。
这是头文件:/* lib/bus/i2c/i2c.h
* 2012-5-11 16:05:24 */

#ifndef __FILE_LIB_BUS_I2C_I2C_H__
#define __FILE_LIB_BUS_I2C_I2C_H__

#include "NUC1xx.h"
#include "../../common/common.h"

/* 数据传输回调函数。
* 每(准备)传输一个字节都调用一次。
* 参数:
*      seq=> 序号,第一次调用时为0,以后每次调用递增。
*      data => seq==0时写(从机地址+W/R)到data。
*            seq!=0时data是数据。发送就写data,接收就读data。
* 返回值表示下一步的行为:
*   I2C_RT_START => 发送开始信号。
*   I2C_RT_STOP=> 发送停止信号。
*   I2C_RT_REPEAT_START_OR_STOP => 如果有下一次传输,就发送RepeatStart,否则发送Stop。
*   I2C_RT_ACK   => 继续传送,回应ACK。
*   I2C_RT_NACK=> 继续传送,回应NACK。*/
typedef uint8_t (* I2C_transmitCallback)(uint8_t seq,uint8_t * data);

/* 异常回调函数。
* 总线出现异常就会被调用。
* 参数:
*   exception => 异常类型,{I2C_EXCEPTION_NACK,I2C_EXCEPTION_ARBITRATION_LOST}。
* 返回值表示下一步的行为:
*   I2C_RT_START => 发送开始信号。
*   I2C_RT_STOP=> 发送停止信号,终止传输(推荐)。
*   I2C_RT_ACK   => 继续传送,回应ACK。
*   I2C_RT_NACK=> 继续传送,回应NACK。*/
typedef uint8_t (* I2C_exceptionCallback)(uint8_t exception);
enum {I2C_EXCEPTION_NACK,I2C_EXCEPTION_ARBITRATION_LOST};

#define I2C_NO_CALLBACK 0
#define I2C_isCallback(cb) ((cb) != I2C_NO_CALLBACK)

/* 包装变量的结构体。*/
typedef struct
{
      I2C_transmitCallback transmitCallback;
      I2C_transmitCallback nextCallback;
      I2C_exceptionCallback exceptionCallback;
      uint32_t curSeq : 7;
      uint32_t isIdle : 1;
}I2C_t;

/*
* 函数声明。 */
void I2C_init                  (uint8_t which,uint32_t rate);
uint8_t I2C_transmit         (uint8_t which,I2C_transmitCallback cb);
uint8_t I2C_isIdle             (uint8_t which);
void I2C_setNextCallback       (uint8_t which,I2C_transmitCallback cb);
void I2C_setExceptionCallback(uint8_t which,I2C_exceptionCallback cb);
//
void I2C_IRQHandler            (uint8_t which);
void I2C_actionStart         (uint8_t which);
void I2C_actionStop            (uint8_t which);
void I2C_actionAck             (uint8_t which);
void I2C_actionNack            (uint8_t which);
void I2C_actionStart_notInIRQ(uint8_t which);

/* 用7位的地址构造(地址+W/R)字节。*/
#define I2C_addressToByte_write(addr7) ((addr7)<<1)
#define I2C_addressToByte_read(addr7)(((addr7)<<1) | 1)

/*
* 函数返回值。
* RT : return */
enum
{
      I2C_RT_IS_IDLE_NO= 0,
      I2C_RT_IS_IDLE_YES = 1
};
enum
{
      I2C_RT_TRANSMIT_NORMAL = 0,
      I2C_RT_TRANSMIT_BUSY,
      I2C_RT_TRANSMIT_ERROR,
};
enum
{
      I2C_RT_START,
      I2C_RT_REPEAT_START_OR_STOP,
      I2C_RT_STOP,
      I2C_RT_ACK,
      I2C_RT_NACK,
};

/*
* 总线状态。
* ST : status
* MA : master
* MT : master transmit
* MR : master receive */
enum
{
    I2C_ST_MA_STARTED          = 0x08,
    I2C_ST_MA_REPEAT_STARTED   = 0x10,
    I2C_ST_MT_ADDR_ACK         = 0x18,
    I2C_ST_MT_ADDR_NACK      = 0x20,
    I2C_ST_MT_DATA_ACK         = 0x28,
    I2C_ST_MT_DATA_NACK      = 0x30,
    I2C_ST_MA_ARBITRATION_LOST = 0x38,
    I2C_ST_MR_ADDR_ACK         = 0x40,
    I2C_ST_MR_ADDR_NACK      = 0x48,
    I2C_ST_MR_DATA_ACK         = 0x50,
    I2C_ST_MR_DATA_NACK      = 0x58,
    I2C_ST_ERROR               = 0x00,
};

#endif // __FILE_LIB_BUS_I2C_I2C_H__
然后是代码:/* lib/bus/i2c/i2c.c
* 2012-5-11 16:04:52 */

#include "NUC1xx.h"
#include "../../common/common.h"
#include "i2c.h"

/*
* 定义变量*/
I2C_T * const I2C_Base = {I2C0,I2C1};
volatile static I2C_t I2C_var;

/*
* 初始化I2C。
* 参数
*   which => 指定总线,{0,1}。
*   rate=> 时钟,kHz,。基于48Mhz的系统时钟。*/
void I2C_init(uint8_t which,uint32_t rate)
{
      if(which == 0)
      {
                /*
               * 设置针脚功能。 */
                SYS->GPAMFP.I2C0_SCL = 1;
                SYS->GPAMFP.I2C0_SDA = 1;
                /*
               * 允许中断。 */
                NVIC_ISER->I2C0_INT = 1;
                /*
               * 通入时钟。 */
                SYSCLK->APBCLK.I2C0_EN = 1;
      }
      else if(which == 1)
      {
                /*
               * 设置针脚功能。 */
                SYS->GPAMFP.I2C1_SCL_nRD = 1;
                SYS->GPAMFP.I2C1_SDA_nWR = 1;
                SYS->ALTMFP.EBI_EN = 0;
                /*
               * 允许中断。 */
                NVIC_ISER->I2C1_INT = 1;
                /*
               * 通入时钟。 */
                SYSCLK->APBCLK.I2C1_EN = 1;
      }
      I2C_var.isIdle = 1;
      I2C_var.transmitCallback = I2C_NO_CALLBACK;
      I2C_var.nextCallback = I2C_NO_CALLBACK;
      I2C_var.exceptionCallback = I2C_NO_CALLBACK;
      /*
         * 设置时钟。 */
      I2C_Base->u32I2CLK = 12000 / rate - 1;
}

/* 判断I2C是否空闲。
* 参数
*   which => 指定总线,{0,1}。*/
uint8_t I2C_isIdle(uint8_t which)
{
      return I2C_var.isIdle;
}

/*
* 传送数据,异步。
* 参数
*   which => 指定总线,{0,1}。
*   cb    => 回调函数,传送是发送还是接收,取决于回调函数的具体实现。
* 返回值
*   I2C_RT_TRANSMIT_NORMAL => 正常。但不保证发送成功。
*   I2C_RT_TRANSMIT_BUSY   => 总线忙。
*   I2C_RT_TRANSMIT_ERROR=> 指定的总线不存在。*/
uint8_t I2C_transmit(uint8_t which,I2C_transmitCallback cb)
{
      if(!I2C_isIdle(which))
                return I2C_RT_TRANSMIT_BUSY;
      I2C_var.isIdle = 0;
      //
      if(which > 1)
                return I2C_RT_TRANSMIT_ERROR;
      //
      I2C_var.transmitCallback = cb;
      I2C_var.nextCallback = I2C_NO_CALLBACK;
      I2C_var.exceptionCallback = I2C_NO_CALLBACK;
      //
      I2C_actionStart(which);
      return I2C_RT_TRANSMIT_NORMAL;
}

/*
* 设置下一个任务。
* 用于连续传输,中间是Repeat Start,而不是Stop。
* 本次传输有效,下次调用I2C_transmit()时会重置。 */
void I2C_setNextCallback(uint8_t which,I2C_transmitCallback cb)
{
      I2C_var.nextCallback = cb;
}

/*
* 设置错误回调函数。
* 详细见I2C_transmitCallback的定义。
* 本次传输有效,下次调用I2C_transmit()时会重置。 */
void I2C_setExceptionCallback(uint8_t which,I2C_exceptionCallback cb)
{
      I2C_var.exceptionCallback = cb;
}

/*
* I2C通用中断处理函数。
* 参数
*   which => 指定总线。 */
void I2C_IRQHandler(uint8_t which)
{
      /*
         * 根据总线状态决定下一步动作。 */
      uint8_t action = I2C_RT_REPEAT_START_OR_STOP;
      switch(I2C_Base->I2CSTATUS)
      {
      case I2C_ST_MA_STARTED :
      case I2C_ST_MA_REPEAT_STARTED :
                /* Start信号已发出,初始化计数,发送(地址+W/R)。 */
                I2C_var.curSeq = 0;
                action = I2C_var.transmitCallback(I2C_var.curSeq,
                              (uint8_t *)&(I2C_Base->u32I2CDAT));
                break;
      case I2C_ST_MT_ADDR_ACK :
      case I2C_ST_MT_DATA_ACK :
      case I2C_ST_MR_DATA_ACK :
                /* 处理数据和获取下一步动作。 */
                I2C_var.curSeq ++;
                action = I2C_var.transmitCallback(I2C_var.curSeq,
                              (uint8_t *)&(I2C_Base->u32I2CDAT));
                break;
      case I2C_ST_MR_ADDR_ACK :
                /* 接收模式,发送完地址,直接ACK就可以。 */
                action = I2C_RT_ACK;
                break;
      case I2C_ST_MT_ADDR_NACK :
      case I2C_ST_MT_DATA_NACK :
      case I2C_ST_MR_ADDR_NACK :
                /* 发送数据后收到NACK,默认动作是Stop。 */
                if(I2C_isCallback(I2C_var.exceptionCallback))
                        action = I2C_var.exceptionCallback(I2C_EXCEPTION_NACK);
                else
                        action = I2C_RT_REPEAT_START_OR_STOP;
                break;
      case I2C_ST_MR_DATA_NACK :
                /* 如果接收数据时响应了NACK,自动Stop。 */
                action = I2C_RT_REPEAT_START_OR_STOP;
                break;
      case I2C_ST_MA_ARBITRATION_LOST :
                /* 总线失控,默认等待重新开始。*/
                if(I2C_isCallback(I2C_var.exceptionCallback))
                        action = I2C_var.exceptionCallback(I2C_EXCEPTION_ARBITRATION_LOST);
                else
                        action = I2C_RT_START;
                break;
      case I2C_ST_ERROR :
      default :
                action = I2C_RT_STOP;
                break;
      }
      /*
         * 执行动作。 */
      switch(action)
      {
      case I2C_RT_START :
                I2C_actionStart(which);
                break;
      case I2C_RT_REPEAT_START_OR_STOP :
                /* 检查有没有连续的任务,有就Repeat Start,否则Stop。 */
                if(I2C_isCallback(I2C_var.nextCallback))
                {
                        I2C_var.transmitCallback = I2C_var.nextCallback;
                        I2C_var.nextCallback = I2C_NO_CALLBACK;
                        I2C_actionStart(which);
                }
                else
                {
                        I2C_actionStop(which);
                }
                break;
      case I2C_RT_STOP :
                I2C_actionStop(which);
                break;
      case I2C_RT_ACK :
                I2C_actionAck(which);
                break;
      case I2C_RT_NACK :
                I2C_actionNack(which);
                break;
      }
}

/*
* I2C0中断处理函数。 */
void I2C0_IRQHandler(void)
{
      I2C_IRQHandler(0);
}

/*
* I2C1中断处理函数。 */
void I2C1_IRQHandler(void)
{
      I2C_IRQHandler(1);
}

void I2C_actionStart(uint8_t which)
{
      I2C_Base->u32I2CON = (_BV(3)|_BV(6)|_BV(7)|_BV(5));
}

void I2C_actionStop(uint8_t which)
{
      I2C_Base->u32I2CON = (_BV(3)|_BV(6)|_BV(7)|_BV(4));
      I2C_var.isIdle = 1;
}

void I2C_actionAck(uint8_t which)
{
      I2C_Base->u32I2CON = (_BV(3)|_BV(6)|_BV(7)|_BV(2));
}

void I2C_actionNack(uint8_t which)
{
      I2C_Base->u32I2CON = (_BV(3)|_BV(6)|_BV(7));
}

void I2C_actionStart_notInIRQ(uint8_t which)
{
      I2C_Base->u32I2CON = (_BV(6)|_BV(7)|_BV(5));
}
接口只有5个函数:void I2C_init                  (uint8_t which,uint32_t rate);
uint8_t I2C_transmit         (uint8_t which,I2C_transmitCallback cb);
uint8_t I2C_isIdle             (uint8_t which);
void I2C_setNextCallback       (uint8_t which,I2C_transmitCallback cb);
void I2C_setExceptionCallback(uint8_t which,I2C_exceptionCallback cb);

Name_006 发表于 2012-6-24 18:54:54

收藏下过些天研究下STM32的硬件IIC                                                                                                                           

MrLCL 发表于 2012-6-25 00:16:45

Name_006 发表于 2012-6-24 18:54 static/image/common/back.gif
收藏下过些天研究下STM32的硬件IIC                                                                   ...

me,too.不过我是研究stm32基础

8s209 发表于 2012-7-5 08:03:39

留待备用
页: [1]
查看完整版本: 基于中断的I2C库