[嵌入式开发模块]MC9S12XEP100 IIC模块 驱动程序

此为本人写的MC9S12XEP100的IIC集成电路总线的硬件驱动程序。


前言

相关理论

相关理论请自行参考数据手册。
此为我对数据手册IIC模块部分的翻译:https://blog.csdn.net/lin_strong/article/details/80259571

驱动模块简介

整个模块是中断驱动的,ISR的运行逻辑基本就是照着数据手册中给出的框图。

这里稍微有一点要注意的,就是在主机接收器的寻址周期(即图左边中间那个Master Rx)结束时不是要切换Rx mode并虚读么,这个时候应该要判断下下一个读的字节是不是最后一个,并据此来设置TXAK,而不应该像图上那样直接就触发下一个接收了,当然,驱动程序中已经写了这个逻辑了。

另外当前模块并不支持10bit 地址,主要就是暂时用不到,懒得写。

模块提供了模块初始化,主机发送/主机接收,函数注册的接口。
中断与主机接口之间采用信号量的方式进行通讯,主机接口先进行会导致中断的操作,然后阻塞地pend信号量;IIC的中断会驱动着完成后续的工作,然后post信号量并通知结果;然后主机接口就会成功pend到信号量,并得知操作结果。

模块内部提供了默认的信号量实现,当在RTOS中运行时,可以通过函数注册接口把操作系统提供的信号量函数给模块使用,这样就可以最大化内核的使用,减少无意义的任务切换开销。后面提供了对uCOS-II进行适配的函数。

从机功能则是完全由中断驱动的,当使用从机功能时,要求用户按照声明的函数提供具体实现,当发生从机接收/从机发送时,对应的函数会被调用,以传递给用户刚刚收到的数据,或从用户处获取下一个要发送的数据。

头文件的配置中提供了一些宏以实现按照需求对代码进行精简,以及对模块进行配置。主要要记得根据自己的CPU频率修改IIC_INIT_IBFD的值

由于模块是中断驱动的,一定要记得把中断向量指向中断服务例程IIC_ISR,并启用中断,使用uCOS-II的时候则要把中断向量指向.s文件中的IIC_uC_ISR。

代码

驱动模块

头文件:

/*
*******************************************************************************************
*
*                                    IIC INTERFACE
*                                       IIC接口
*
* File : IIC.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/03/26
* version: V1.4
* History: 2018/05/07  V1.0   the prototype
*          2018/05/15  V1.1   add the slave part of IIC.
*                             add the functions register, so user can change the behaviour
*                             of the module.
*                      V1.2   a tip on the ISR
*          2018/05/20  V1.3   figure out the method to make ISR in this module and in IIC_uCos
*                             compatible.
*          2019/03/26  V1.4   some modification to the malloc configuration.
* NOTE(s):  1. don't support 10-bit address for now.
*           2. this module is ISR-drived, so you must point the IIC_ISR to the corresponding
*              address and enable interrupt.
*           3. note that the funcitons in this module is not thread-safe.
*********************************************************************************************
*/

#ifndef   IIC_H
#define   IIC_H

/*
********************************************************************************************
*                                   INCLUDES
********************************************************************************************
*/

#include "common.h"

/*
******************************************************************************************
*                                    CONSTANT
******************************************************************************************
*/

// IIC address length
#define IIC_ADDRLEN_7BIT        0
#define IIC_ADDRLEN_10BIT       1

/*
*******************************************************************************************
*                               CONFIGURE 主配置
*******************************************************************************************
*/

// to exclude code for IIC slave mode.
// #define IIC_SLAVEMODE_DISABLE

// to exclude code for IIC master Rx mode.
// #define IIC_MASTER_RX_DISABLE

// to exclude code for IIC master Tx mode.
// #define IIC_MASTER_TX_DISABLE

// to enable debug message through standard printf
// #define _DEBUG

//************ 初始化配置 ***************//
// 根据手册设置分频寄存器
#ifndef IIC_INIT_IBFD
#define IIC_INIT_IBFD        0x94              // 总线时钟32MHz,设置SCL主频为100KHz
#endif
// IIC模块使用的地址长度
#ifndef IIC_INIT_ADDRLEN
#define IIC_INIT_ADDRLEN     IIC_ADDRLEN_7BIT  // 当前只支持7位地址,不支持10位的
#endif
// IIC模块使用的从机地址(定义在低7bits),如果启用了从机代码
#ifndef IIC_INIT_SLAVEADDR
#define IIC_INIT_SLAVEADDR   0x37
#endif
// 在等待模式下内部时钟是否停止
#ifndef IIC_INIT_STOPINWAIT
#define IIC_INIT_STOPINWAIT  TRUE
#endif

/*
****************************************************************************************
*                                  ERROR CODES
****************************************************************************************
*/

#define IIC_ERR_NULL    0
#define IIC_ERR_AUG     1      // 参数错误
#define IIC_ERR_NOACK   2      // 未收到答复
#define IIC_ERR_IBAL    3      // 仲裁丢失
#define IIC_ERR_IBB     4      // 总线忙
#define IIC_ERR_TIMEOUT 5      // 等待超时
#define IIC_ERR_UNKNOWN 6      // 未知错误
/*
******************************************************************************************
*                                  TYPE DEFINE
******************************************************************************************
*/

// Description: IIC内部阻塞等待时使用的函数,比如可以在其中添加线程Dly函数来实现阻塞等待时放弃CPU时间
// Arguments  : wCnt     当前等待次数计数
// return     : TRUE     继续等待
//              FALSE    停止等待,返回错误
typedef unsigned char (* IIC_FUNC_WAITFUNC)(unsigned long wCnt);

// IIC内部信号量相关函数,当使用操作系统时可以替换为操作系统的信号量

// Description: 等待信号量
// Arguments  : 
// return     : TRUE     成功pend到信号量
//              FALSE    等待超时或其他错误
typedef unsigned char (* IIC_FUNC_SEM_PEND)(void);
// Description: 发送信号量
// Arguments  : 
// return     : 
typedef void (* IIC_FUNC_SEM_POST)(void);
// Description: 重置信号量
// Arguments  : 
// return     : 
typedef void (* IIC_FUNC_SEM_RESET)(void);

/*
************************************************************************************
*                          FUNCTION PROTOTYPES  函数原型
************************************************************************************
*/

unsigned char IIC_Init(void);
#define IIC_ReceiveChar(calAddr,pChar)   IIC_Recv(calAddr,pChar,1)
unsigned char IIC_Recv(unsigned char calAddr,unsigned char *rBuf,unsigned short len);
#define IIC_SendChar(calAddr,pChar)      IIC_Send(calAddr,pChar,1)
unsigned char IIC_Send(unsigned char calAddr,unsigned char *sBuf,unsigned short len);

void IIC_FuncReg_Wait(IIC_FUNC_WAITFUNC f);
void IIC_FuncReg_Sem(IIC_FUNC_SEM_RESET r,IIC_FUNC_SEM_POST pt,IIC_FUNC_SEM_PEND pd);


// 启用从机时要求用户实现的函数
// 注意,这些函数是在ISR中被调用的
// Description: when is in slave Tx mode, to get the next byte to send from user.
// Argument : No    the number of current byte of this conversation. begin from 0;
// return   : the byte to send.
extern unsigned char IIC_Send_AsSlave(unsigned short No);
// Description: when is in slave Rx mode, to pass the next byte received to user.
// Argument : No    the number of current byte of this conversation. begin from 0;
//            c     the data recevied.
// return   : 
extern void IIC_Recv_AsSlave(unsigned short No,unsigned char c);

// ISR 中断服务例程
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
interrupt void near IIC_ISR(void);
#pragma pop

/*
************************************************************************************
*                          ERROR CHECK 错误检查
************************************************************************************
*/
#ifdef IIC_SLAVEMODE_DISABLE
  #ifdef IIC_MASTER_RX_DISABLE
    #ifdef IIC_MASTER_TX_DISABLE
      #error "can't exclude all slave and master code."
    #endif
  #endif
#endif

#endif  // of IIC_H

源文件:

/*
*******************************************************************************************
*
*                      MC9S12XEP100 IMPLEMENTATION OF IIC INTERFACES
*                               IIC接口的MC9S12XEP100实现
*
* File : IIC.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/03/26
* version: V1.4
* History: 2018/05/07  V1.0   the prototype
*          2018/05/15  V1.1   add the slave part of IIC.
*                             add the functions register, so user can change the behaviour
*                             of the module.
*                      V1.2   a tip on the ISR
*          2018/05/20  V1.3   figure out the method to make ISR in this module and in IIC_uCos
*                             compatible.
*          2019/03/26  V1.4   some modification to the malloc configuration.
*                             some modification to the code to eliminate compiler warning.
* NOTE(s):  1. don't support 10-bit address for now.
*           2. this module is ISR-drived, so you must point the IIC_ISR to the corresponding
*              address and enable interrupt.
*           3. note that the funcitons in this module is not thread-safe.
*********************************************************************************************
*/

/*
*********************************************************************************************
*                                       INCLUDES
*********************************************************************************************
*/

#include <stddef.h>
#include "IIC.h"
#include <MC9S12XEP100.h>

/*
*********************************************************************************************
*                                         CONSTANT
*********************************************************************************************
*/
#define ISR_ERR_NULL    0     // 正常处理完毕
#define ISR_SENDOK      1
#define ISR_ERR_NOACK   2
#define ISR_RECVOK      3
#define ISR_ERR_IBAL    4

#undef  IIC_MASTER_EN
#ifndef IIC_MASTER_RX_DISABLE
#define IIC_MASTER_EN
#endif
#ifndef IIC_MASTER_TX_DISABLE
#define IIC_MASTER_EN
#endif

#ifdef _DEBUG
#include <stdio.h>
#define dbgprintf(msg)             (void)printf(msg)
#else
#define dbgprintf(msg)
#endif
/*
*********************************************************************************************
*                                  LOCAL FUNCTION DECLARATION
*********************************************************************************************
*/
// 发起启动条件,默认当前为从机模式,如总线忙则会返回错误,后面需要软件查看IBIF来看是否成功
// CalAddr:主叫地址(D0:R/W)
static unsigned char _IIC_StartCondtion(unsigned char CalAddr);

// 默认的等待函数,无限等待
static unsigned char _IIC_Wait(unsigned long wCnt){ (void)wCnt; return TRUE;}

static unsigned char _sem;
// 默认使用的信号量函数
static unsigned char _IIC_SemPend(void){ 
  while(_sem == 0); // 等待中断发来结果
  _sem--;
  return TRUE;
}
static void _IIC_SemPost(void){ _sem++;}
static void _IIC_SemReset(void){ _sem = 0;}
/*
*********************************************************************************************
*                                  LOCAL VARIABLE
*********************************************************************************************
*/

static unsigned char* pTxRxBuf; // 指向主机使用的缓冲区
static unsigned short TxCnt;    // 等待发送的字节个数
#define _LastByteTransmitted() (TxCnt == 0)
#define _SendNextByte()        {TxCnt--; IIC0_IBDR = *pTxRxBuf++;}     // 发送下一个字节
static unsigned short RxCnt;    // 等待接收的字节个数
#define _isLastByteToRead()    (RxCnt == 1)
#define _isLast2ndToRead()     (RxCnt == 2)
#define _RecvNextByte()        {RxCnt--; *pTxRxBuf++ = IIC0_IBDR;}     // 接收下一个字节
static unsigned char isAddrCyc_MR; // whether in address cycle(for Master Rx)
static unsigned char iicRst;       // 存放上次的结果
static unsigned short No_Slave;    // 计数作为从机发送/接收到第几个字符了 

static IIC_FUNC_WAITFUNC _Wait;
static IIC_FUNC_SEM_PEND _SemPend;
static IIC_FUNC_SEM_POST _SemPost;
static IIC_FUNC_SEM_RESET _SemRst;

/*
*********************************************************************************************
*                                        IIC_Init()
*
* Description : Initialize IIC support hardware(marco style).  初始化IIC硬件
*
* Arguments   : 
*
* Return      : IIC_ERR_NULL       if success.
*
* Note(s)     : 
*********************************************************************************************
*/
unsigned char IIC_Init(){
  IIC0_IBFD = IIC_INIT_IBFD;
  IIC0_IBCR_IBEN = 1; // 使能IIC模块,然后才能设置IBCR的其他位
  IIC0_IBSR_IBAL = 1; // 清除IBAL标志位  
  IIC0_IBCR_IBIE = 1; // 使能中断
#if(IIC_INIT_STOPINWAIT == TRUE)
  IIC0_IBCR_IBSWAI = 1;
#endif
#ifndef IIC_SLAVEMODE_DISABLE
  IIC0_IBAD = IIC_INIT_SLAVEADDR << 1;  // 写入自己的从机地址
#endif
  _Wait = _IIC_Wait;
  _SemPend = _IIC_SemPend;
  _SemPost = _IIC_SemPost;
  _SemRst = _IIC_SemReset;
  return IIC_ERR_NULL;
}

/*
*********************************************************************************************
*                                        IIC_Recv()
*
* Description : Receive several bytes from slave.
*
* Arguments   : calAddr       the calling address of the slave.
*               rBuf          point to the buffer which will hold the result.
*               len           the number of bytes needed to be received.
*
* Return      : IIC_ERR_NULL       if success.
*               IIC_ERR_NOACK      if no ack from slave.
*               IIC_ERR_IBAL       if arbitration lost
*               IIC_ERR_TIMEOUT    if timeout for pending semaphore
*               IIC_ERR_UNKNOWN    if unknown err;
* Note: 
*********************************************************************************************
*/

unsigned char IIC_Recv(unsigned char calAddr,unsigned char *rBuf,unsigned short len){
  unsigned char err;
  if(len == 0)                // 收0个数据没有意义,直接退出
    return IIC_ERR_AUG;
  // 初始化参数
  isAddrCyc_MR = 1;
  pTxRxBuf = rBuf;
  RxCnt = len;
  TxCnt = (unsigned short)-1;
  _SemRst();
  // 产生启动信号并发送主叫地址+读指令(bit 0 == 1)
  err = _IIC_StartCondtion((calAddr << 1) | 0x01);
  if(err)            // 如果发生错误,返回错误
    return err;
  if(!_SemPend())    // 等待结果,如果超时,返回超时错误
    return IIC_ERR_TIMEOUT;
  switch(iicRst){
    case ISR_ERR_NOACK:
      return IIC_ERR_NOACK;
    case ISR_ERR_IBAL:
      return IIC_ERR_IBAL;
    case ISR_RECVOK:
      return IIC_ERR_NULL;
    default:
      return IIC_ERR_UNKNOWN;
  }
}
/*
*********************************************************************************************
*                                       IIC_Send()
*
* Description : Send several bytes to slave.
*
* Arguments   : calAddr       the calling address of the slave.
*               rBuf          point to the buffer which contains the data to be send.
*               len           the number of bytes needed to be send.
*
* Return      : IIC_ERR_NULL       if success.
*               IIC_ERR_NOACK      if no ack from slave.
*               IIC_ERR_IBAL       if arbitration lost
*               IIC_ERR_TIMEOUT    if timeout for pending semaphore
*               IIC_ERR_UNKNOWN    if unknown err;
* Note: 
*********************************************************************************************
*/
unsigned char IIC_Send(unsigned char calAddr,unsigned char *sBuf,unsigned short len){
  volatile unsigned char err;
  unsigned long wcnt = 0;
  if(len == 0)                // 发0个数据没有意义,直接退出
    return IIC_ERR_AUG;
  // 初始化参数
  isAddrCyc_MR = 0;
  pTxRxBuf = sBuf;
  RxCnt = 0;
  TxCnt = len;
  _SemRst();
  // 产生启动信号并发送主叫地址+写指令(bit 0 == 0)
  err = _IIC_StartCondtion(calAddr << 1);
  if(err)            // 如果发生错误,返回错误
    return err;
  if(!_SemPend())    // 等待结果
    return IIC_ERR_TIMEOUT;
  switch(iicRst){
    case ISR_ERR_NOACK:
      return IIC_ERR_NOACK;
    case ISR_ERR_IBAL:
      return IIC_ERR_IBAL;
    case ISR_SENDOK:
      return IIC_ERR_NULL;
    default:
      return IIC_ERR_UNKNOWN;
  }
}
/*
*********************************************************************************************
*                                  IIC_FuncReg_Wait()
*
* Description : function register of wait func.
*
* Arguments   : f       the function used for block wating.
*
* Return      : 
*
* Note: 
*********************************************************************************************
*/
void IIC_FuncReg_Wait(IIC_FUNC_WAITFUNC f){
  _Wait = f;
}
/*
*********************************************************************************************
*                                   IIC_FuncReg_Sem()
*
* Description : function register of semaphore funcs.
*
* Arguments   : r       the function used to reset semaphore.
*               pt      the function used to post semaphore.
*               pd      the function used to pend semaphore.
* Return      : 
*
* Note: 
*********************************************************************************************
*/
void IIC_FuncReg_Sem(IIC_FUNC_SEM_RESET r,IIC_FUNC_SEM_POST pt,IIC_FUNC_SEM_PEND pd){
  _SemRst = r;
  _SemPost = pt;
  _SemPend = pd;
}

/*
*********************************************************************************************
*                                  LOCAL FUNCTION
*********************************************************************************************
*/
// 发起启动条件
static unsigned char _IIC_StartCondtion(unsigned char CalAddr){
  unsigned long wcnt = 0;
  IIC0_IBSR_IBIF = 1;      // 清零中断标志位
  IIC0_IBCR_TX_RX = 1;     // 设置单片机为发送模式
  while(IIC0_IBSR_IBB){    // 检查总线状态直到结束
    if(!_Wait(wcnt++)) 
      return IIC_ERR_IBB;
  };
  IIC0_IBCR_MS_SL = 1;     // 设置主机传输模式;即生成启动信号
  while(!IIC0_IBSR_IBB){   // 等待IBB标志位置位
    if(!_Wait(wcnt++))
      return IIC_ERR_IBB;
  };    
  IIC0_IBDR = CalAddr;     // 传输主叫地址,D0=R/W
  while(!IIC0_IBSR_IBB){   // 等待IBB标志位置位
    if(!_Wait(wcnt++))
      return IIC_ERR_IBB;
  };
  return IIC_ERR_NULL;
}
// 中断中通知主机函数的执行结果
static void _notifyRst(unsigned char rst){
  iicRst = rst;
  _SemPost();
  return;
}
// 主机发送中断
static void _IIC_ISR_MasterTx(void){
  unsigned char data;
  if(_LastByteTransmitted()){       // 如果发送完最后一个字节
    IIC0_IBCR_MS_SL = 0; // 那么生成停止信号
    _notifyRst(ISR_SENDOK);
    return;
  }
  if(IIC0_IBSR_RXAK != 0){ //如果发送后没有应答
    IIC0_IBCR_MS_SL = 0; // 那么生成停止信号
    _notifyRst(ISR_ERR_NOACK);
    return;
  }
  if(isAddrCyc_MR) {               // 如果是主接收的地址周期
    isAddrCyc_MR = 0;
    IIC0_IBCR_TX_RX = 0;           // 切换单片机为接收模式
    IIC0_IBCR_TXAK = (RxCnt <= 1); // 如果是最后一个要接收的数据,接收完后不应答,否则应答
    data = IIC0_IBDR;              // 虚读数据寄存器,启动接收第一个字节
    return;
  }else{
    _SendNextByte();            // 发送下一个字节
    return;
  }
}
// 主机接收中断
static void _IIC_ISR_MasterRx(void){
#ifdef _DEBUG
  if(RxCnt == 0){
    dbgprintf("IIC Err,RxCnt == 0.\r\n");  // 这里不应该出现0,出现了说明程序有bug
  }
#endif
  if(_isLastByteToRead()){  // 如果是要读的最后一个字节
    IIC0_IBCR_MS_SL = 0;    // 那么生成停止信号
    _notifyRst(ISR_RECVOK);
  }else if(_isLast2ndToRead()){         // 是倒数第二个要读取的数据时设置不应答
    IIC0_IBCR_TXAK = 1;
  }
  _RecvNextByte();          // 读取数据进行存储,如果不是最后一个字节的话会发起下一个接收
  return;
}
// 地址匹配中断
static void _IIC_ISR_Addressed(void){
  unsigned char data;
  No_Slave = 0;
  if(IIC0_IBSR_SRW){          // 如果主机想读数据
    IIC0_IBCR_TX_RX = 1;      // 则作为从机应该发送数据
    IIC0_IBDR = IIC_Send_AsSlave(0);  // 获取第一个要发送的字符并发送
  }else{                      // 如果主机想发数据
    IIC0_IBCR_TX_RX = 0;      // 则作为从机应该接收数据
    IIC0_IBCR_TXAK = 0;       // 对所有数据进行应答
    data = IIC0_IBDR;         // 虚读以开始接收
  }
}
// 从机接收中断
static void _IIC_ISR_SlaveRx(void){
  unsigned char data;
  data = IIC0_IBDR;
  IIC_Recv_AsSlave(No_Slave++,data);
}
// 从机发送中断
static void _IIC_ISR_SlaveTx(void){
  unsigned char data;
  if(IIC0_IBSR_RXAK != 0){    //如果发送后没有应答
    IIC0_IBCR_TX_RX = 0;      // 则切换到接收模式
    data = IIC0_IBDR;         // 并虚读一次
  }else{                      // 如果收到应答的话
    IIC0_IBDR = IIC_Send_AsSlave(++No_Slave);  // 向用户要下一个字节来发送
  }
}
/*
*********************************************************************************************
*                                          ISR
*********************************************************************************************
*/

#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED

void near _IIC_ISR(void){
  IIC0_IBSR_IBIF = 1;
  if(IIC0_IBCR_MS_SL){       // 如果当前为主机
    if(IIC0_IBCR_TX_RX){      // 且TX/RX置位
#ifndef IIC_MASTER_TX_DISABLE
      _IIC_ISR_MasterTx();   // 则为主机发送中断
#else
      dbgprintf("IIC Err,发生主机发送中断.\r\n");
#endif
    }else{     // 否则
#ifndef IIC_MASTER_RX_DISABLE
      _IIC_ISR_MasterRx();   // 为主机接收中断
#else
      dbgprintf("IIC Err,发生主机接收中断.\r\n");
#endif
    }
  }else{                     // 如果当前为从机
    if(IIC0_IBSR_IBAL){      // 如果发生仲裁丢失
      IIC0_IBSR_IBAL = 1;    // 清零标志位
#ifdef IIC_MASTER_EN
      _notifyRst(ISR_ERR_IBAL); // 通知仲裁丢失
      if(IIC0_IBSR_IAAS == 0) // 如果没有被作为从机寻址
        return;               // 则直接退出
#else
      dbgprintf("IIC Err,发生主机仲裁丢失中断.\r\n");
#endif
    }
#ifndef IIC_SLAVEMODE_DISABLE
    if(IIC0_IBSR_IAAS){  // 如果被作为从机寻址
      _IIC_ISR_Addressed();   // 为地址匹配发生的中断,(当前不支持10位地址)
      return;
    }
    if(IIC0_IBCR_TX_RX)
      _IIC_ISR_SlaveTx();
    else
      _IIC_ISR_SlaveRx();
#else
    dbgprintf("IIC错误,发生从机中断.\r\n");
#endif
  }
}

interrupt void near IIC_ISR(void){
  _IIC_ISR();
}
#pragma pop

基于UCOS-II的驱动

头文件:

/*
*******************************************************************************************
*
*
*                                IIC SUPPORT PACKAGE
*                                  for uC/OS - II
*
* File : IIC_uCos.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/05/20
* version: V1.1
* History: 2018/05/15  V1.0   the prototype
*          2018/05/20  V1.1   a little modification for ISR
* NOTE(s):  1. This module is based on the my IIC driver module for MC9S12XEP100.
*           2. it give the example of adapting the IIC driver to uC/OS-II RTOS.
*           3. to use iic in uCos-II, you should point IIC_uC_ISR in IIC.s to the corresponding
*              address and enable interrupt, not the IIC_ISR.
*           4. the module is only useful when you use IIC as master.
*********************************************************************************************
*/

#ifndef   IIC_UCOS_H
#define   IIC_UCOS_H

/*
********************************************************************************************
*                                   INCLUDE
********************************************************************************************
*/

#include "IIC.h"
#include "ucos_ii.h"

/*
*******************************************************************************************
*                               CONFIGURE 主配置
*******************************************************************************************
*/

#define IIC_UCOS_WAIT_MAX     200   // 最长等待多少次TICK
#define IIC_UCOS_SEMPEND_MAX  200   // 最久等待信号量多久个TICK

/*
************************************************************************************
*                          FUNCTION PROTOTYPES  函数原型
************************************************************************************
*/

void IIC_uCos_Init(void);

/*
************************************************************************************
*                          ERROR CHECK 错误检查
************************************************************************************
*/

#endif  // of IIC_UCOS_H

源文件:

/*
*******************************************************************************************
*
*
*                                IIC SUPPORT PACKAGE
*                                  for uC/OS - II
*
* File : IIC_uCos.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/05/20
* version: V1.1
* History: 2018/05/15  V1.0   the prototype
*          2018/05/20  V1.1   a little modification for ISR
* NOTE(s):  1. This module is based on the my IIC driver module for MC9S12XEP100.
*           2. it give the example of adapting the IIC driver to uC/OS-II RTOS.
*           3. to use iic in uCos-II, you should point IIC_uC_ISR in IIC.s to the corresponding
*              address and enable interrupt, not the IIC_ISR.
*           4. the module is only useful when you use IIC as master.
*********************************************************************************************
*/

/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/
#include <stddef.h>
#include "IIC_uCos.h"

/*
*********************************************************************************************************
*                                  LOCAL FUNCTION DECLARATION
*********************************************************************************************************
*/
// functions for register
static unsigned char iic_wait(unsigned long wCnt);
static unsigned char iic_sem_pend(void);
static void iic_sem_post(void);
static void iic_sem_reset(void);

/*
*********************************************************************************************************
*                                  LOCAL VARIABLE
*********************************************************************************************************
*/

static OS_EVENT* iic_sem;

/*
*********************************************************************************************************
*                                        IIC_uCos_Init()
*
* Description : Initialize IIC for uCos-II.  初始化
*
* Arguments   : 
*
* Return      : IIC_ERR_NULL       if success.
*
* Note(s)     : 
*********************************************************************************************************
*/
void IIC_uCos_Init(){
#if(IIC_MASTER_RX_EN || IIC_MASTER_TX_EN)
  iic_sem = OSSemCreate(0);
  m_assert(iic_sem != NULL,"给iic分配信号量时出现错误,信号量不够用。\r\n");
  IIC_FuncReg_Wait(iic_wait);
  IIC_FuncReg_Sem(iic_sem_reset,iic_sem_post,iic_sem_pend);
#endif
}

/*
*********************************************************************************************************
*                                  LOCAL FUNCTION 
*********************************************************************************************************
*/
// Description: IIC内部阻塞等待时使用的函数,比如可以在其中添加线程Dly函数来实现阻塞等待时放弃CPU时间
// Arguments  : wCnt     当前等待次数计数
// return     : TRUE     继续等待
//              FALSE    停止等待,返回错误
unsigned char iic_wait(unsigned long wCnt){
  OSTimeDly(1);
  if(wCnt > IIC_UCOS_WAIT_MAX)   // 计时两百次都没等待成功就停止阻塞返回错误
    return FALSE;
  else
    return TRUE;
}

// IIC内部信号量相关函数,当使用操作系统时可以替换为操作系统的信号量

// Description: 等待信号量
// Arguments  : 
// return     : TRUE     成功pend到信号量
//              FALSE    等待超时或其他错误
unsigned char iic_sem_pend(void){
  INT8U err;
  OSSemPend(iic_sem,IIC_UCOS_SEMPEND_MAX,&err);
  return err == OS_ERR_NONE;
}
// Description: 发送信号量
// Arguments  : 
// return     : 
void iic_sem_post(void){
  OSSemPost(iic_sem);
}
// Description: 重置信号量
// Arguments  : 
// return     : 
void iic_sem_reset(void){
  INT8U err;
  OSSemSet(iic_sem,0,&err);
}

按UCOS要求写的中断函数:

;********************************************************************************************************
;                                               uC/OS-II
;                                         The Real-Time Kernel
;
;                         (c) Copyright 2002, Jean J. Labrosse, Weston, FL
;                                          All Rights Reserved
;
;
;                                       PAGED S12X Specific code
;                                            (CODEWARRIOR)
;
; File         : IIC_uCos.s
; By           : Lin Shijun(http://blog.csdn.net/lin_strong)
;
; Notes        : THIS FILE *MUST* BE LINKED INTO NON_BANKED MEMORY! 这个文件必须放在非分页内存中
;                    modified according to uC/OS-II's example.  依据uC/OS-II的模版修改。
;********************************************************************************************************

NON_BANKED:       section

;********************************************************************************************************
;                                           I/O PORT ADDRESSES   I/O口地址
;********************************************************************************************************

PPAGE:            equ    $0015         ; Addres of PPAGE register (assuming MC9S12XEP100 part)
RPAGE:            equ    $0016         ; Addres of RPAGE register (assuming MC9S12XEP100 part)
EPAGE:            equ    $0017         ; Addres of EPAGE register (assuming MC9S12XEP100 part)
GPAGE:            equ    $0010         ; Addres of GPAGE register (assuming MC9S12XEP100 part)


;********************************************************************************************************
;                                          PUBLIC DECLARATIONS   公开声明
;********************************************************************************************************

    xdef   IIC_uC_ISR
    
;********************************************************************************************************
;                                         EXTERNAL DECLARATIONS  外部声明
;********************************************************************************************************

    xref   OSIntExit
    xref   OSIntNesting
    xref   OSTCBCur

    xref   _IIC_ISR

    
;********************************************************************************************************
;                                           SCI RxTx ISR
;
; Description : This routine is the uC/Probe RxTx interrupt service routine
;
; Arguments   : none
;
; Notes       : 1) All USER interrupts should be modeled EXACTLY like this where the only
;                  line to be modified is the call to your ISR_Handler and perhaps the call to
;                  the label name SCI0_ISR_Handler.
;********************************************************************************************************

IIC_uC_ISR:
    ldaa   GPAGE                       ; Get current value of GPAGE register
    psha                               ; Push GPAGE register onto current task's stack

    ldaa   EPAGE                       ; Get current value of EPAGE register
    psha                               ; Push EPAGE register onto current task's stack

    ldaa   RPAGE                       ; Get current value of RPAGE register
    psha                               ; Push RPAGE register onto current task's stack

    ldaa   PPAGE                       ; Get current value of PPAGE register
    psha                               ; Push PPAGE register onto current task's stack

    inc    OSIntNesting                ; Notify uC/OS-II about ISR

    ldab   OSIntNesting                ; if (OSIntNesting == 1) {
    cmpb   #$01
    bne    IIC_uC_ISR1

    ldy    OSTCBCur                    ;     OSTCBCur->OSTCBStkPtr = Stack Pointer
    sts    0,y                         ; }

IIC_uC_ISR1:
    JSR    _IIC_ISR                     ; near Call TxRx ISR handler. (See IIC.c)

;   cli                               ; Optionally enable interrupts to allow interrupt nesting

    call   OSIntExit                  ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().

    pula                               ; Get value of PPAGE register
    staa   PPAGE                       ; Store into CPU's PPAGE register

    pula                               ; Get value of RPAGE register
    staa   RPAGE                       ; Store into CPU's RPAGE register

    pula                               ; Get value of EPAGE register
    staa   EPAGE                       ; Store into CPU's EPAGE register

    pula                               ; Get value of GPAGE register
    staa   GPAGE                       ; Store into CPU's GPAGE register

    rti                               ; Return from interrupt to interrupted task.

可以看到,其实将这个模块改到uCOS-II上实际干的事情就只是把uCOS的信号量功能注册给了原先的驱动程序。

示例代码

#include <hidef.h>      /* common defines and macros */
#include "derivative.h"      /* derivative-specific definitions */
#include "IIC.h"
#include <stdio.h>
#include <string.h>

typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect @0xFFC0 = IIC_ISR;

// IIC主从通讯程序
// 通过在编译器选项中定义宏 IIC_SLAVEMODE_DISABLE 来设置当前程序为主机,否则为从机

// 主从机的通讯使用的协议:
// 主机写的时候第一个数据字节修改从机的寄存器指针的值,后面的数据字节则写入从机寄存器
// 主机读的时候则接收当前从机寄存器指针指向的字节
// 每次读/写后,从机的寄存器指针的值自增,增长到底后回到0

#define LED_CPU_DDR  DDRK_DDRK4
#define LED_CPU      PORTK_PK4
#define BUS_CLOCK    32000000
void Delay(void) {
  unsigned int i,j;
  for(i = 0; i < 200; i++)
    for(j = 0; j < 50000; j++)
      ;
}
void INIT_PLL(void) 
{
    CLKSEL &= 0x7f;       //set OSCCLK as sysclk
    PLLCTL &= 0x8F;       //DisaKble PLL circuit
    CRGINT &= 0xDF;
    
    #if(BUS_CLOCK == 40000000) 
      SYNR = 0x44;
    #elif(BUS_CLOCK == 32000000)
      SYNR = 0x43;     
    #elif(BUS_CLOCK == 24000000)
      SYNR = 0x42;
    #endif 

    REFDV = 0x81;         //PLLCLK=2×OSCCLK×(SYNR+1)/(REFDV+1)=64MHz ,fbus=32M
    PLLCTL =PLLCTL|0x70;  //Enable PLL circuit
    asm NOP;
    asm NOP;
    while(!(CRGFLG&0x08)); //PLLCLK is Locked already
    CLKSEL |= 0x80;        //set PLLCLK as sysclk
}

unsigned char TxCmd[] = {0x00,0x33,0x44,0x53,0x44};  // 从0x00开始写寄存器,分别为0x33,0x44,0x53
unsigned char Rxbuf[3];
char strbuf[100];
void main(void) {
  volatile unsigned char err;
  INIT_PLL();
  IIC_Init();
  LED_CPU_DDR = 1;
  LED_CPU = 0;
  EnableInterrupts;
  for(;;) {
    Delay();
#ifdef IIC_SLAVEMODE_DISABLE
    // 从0寄存器开始写入3个字节
    if((err = IIC_Send(IIC_INIT_SLAVEADDR,TxCmd,5)) != IIC_ERR_NULL)
      continue;
    // 指针重新归0
    if((err =IIC_SendChar(IIC_INIT_SLAVEADDR,&TxCmd[0])) != IIC_ERR_NULL)
      continue;
    // 读取三个字节
    if((err =IIC_Recv(IIC_INIT_SLAVEADDR,Rxbuf,3)) != IIC_ERR_NULL)
      continue;
    if(memcmp(&TxCmd[1],Rxbuf,3) != 0)
      continue;
    LED_CPU = !LED_CPU;
    // 每次改变写入的值
    TxCmd[1]++;
    TxCmd[2]++;
    TxCmd[3]++;
#endif
  }
}

static unsigned char RegPointer;  // 从机的寄存器指针
static unsigned char Regs[0x13];  // 从机的寄存器
unsigned char IIC_Send_AsSlave(unsigned short No){
  unsigned char rst;
  rst = Regs[RegPointer++];    
  if(RegPointer >= 0x13)
    RegPointer = 0;
  LED_CPU = !LED_CPU;
  return rst;
}
void IIC_Recv_AsSlave(unsigned short No,unsigned char c){
  if(No == 0){
    if(c < 0x13)
      RegPointer = c;
  }else{
    Regs[RegPointer++] = c;
  }
  LED_CPU = !LED_CPU;
}

这里只给贴出了裸奔程序时使用的代码示例。基于uCOS-II的示例由于要改的地方较杂,就不细讲了。
基本就是先把中断向量指向IIC_uC_ISR,然后初始化时多调用次

  IIC_uCos_Init(); 

就好了。
其他都差不多。

代码下载

注:这里提供下载的代码是V1.3版本的。V1.4中略有修改。IIC_uCos则是1.0版本的,现为1.1版本的。
这里把代码及示例代码提供打包下载。稍微收点分。
https://download.csdn.net/download/lin_strong/10416624

注意,下载的代码中的裸奔程序直接运行时会跑飞,需要在void near IIC_ISR(void);前加一个interrupt才行。
新版本中已经解决了这个问题。


更新历史:
2019/03/26
IIC模块更新到V1.4,修改了头文件中进行配置的方法。对代码进行了一点修改以消除编译器警告。
IIC_uCos模块更新到V1.2,。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值