[嵌入式開發模塊]MC9S12XEP100 IIC模塊 驅動程序

版權聲明:本文爲博主(http://blog.csdn.net/lin_strong)原創文章,轉載請標明出處並事先取得博主同意 https://blog.csdn.net/lin_strong/article/details/80327959

此爲本人寫的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 SUPPORT PACKAGE
*                                Freescale MC9S12XEP100
*                           飛思卡爾   MC9S12XEP100  IIC支持包
*
* File : IIC.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/05/15
* version: V1.2
* 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
* 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.
*           4. important !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*              comment the "interrupt" before the declaration of IIC_ISR, if you don't use 
*              IIC_ISR as ISR directly.
*              for example, if you use the IIC_uC_ISR in IIC_uCos.s as ISR.
*********************************************************************************************
*/

#ifndef   IIC_H
#define   IIC_H

/*
********************************************************************************************
*                                   MISCELLANEOUS
********************************************************************************************
*/

#ifndef  FALSE
#define  FALSE    0
#endif

#ifndef  TRUE
#define  TRUE     1
#endif

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

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

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

#define IIC_SLAVEMODE_EN   FALSE       // TRUE: include code for IIC slave mode.
#define IIC_MASTER_RX_EN   TRUE        // TRUE: include code for IIC master Rx mode.
#define IIC_MASTER_TX_EN   TRUE        // TRUE:include code for IIC master Tx mode.

//************ 初始化配置 ***************//
// 根據手冊設置分頻寄存器
#define IIC_INIT_IBFD        0x94              // 總線時鐘32MHz,設置SCL主頻爲100KHz
// IIC模塊使用的地址長度
#define IIC_INIT_ADDRLEN     IIC_ADDRLEN_7BIT  // 當前只支持7位地址,不支持10位的
// IIC模塊使用的從機地址(定義在低7bits),如果IIC_SLAVEMODE_EN == TRUE
#define IIC_INIT_SLAVEADDR   0x37
// 在等待模式下內部時鐘是否停止
#define IIC_INIT_STOPINWAIT  TRUE

// debug configuration

#define NDEBUG                // comment it when is debugging
// user should give the interface to print error message 
// prototype: void errmsg_print(char* msg);
extern void prinf(const char *str);
#define errmsg_print(msg)   prinf(msg)

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

#include <MC9S12XEP100.h>

/*
****************************************************************************************
*                                  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);


#ifdef NDEBUG
  #define m_assert(cond,errMsg)
#else
  #define m_assert(cond,errMsg) \
  if(!(cond)){  errmsg_print(errMsg); while(1);};
#endif

// ISR 中斷服務例程
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// comment the "interrupt" below if you don't use IIC_ISR as ISR directly.
// for example, if you use the IIC_uC_ISR in IIC_uCos.s as ISR.
interrupt 
void near IIC_ISR(void);
#pragma pop

/*
************************************************************************************
*                          ERROR CHECK 錯誤檢查
************************************************************************************
*/
#if(!IIC_SLAVEMODE_EN && !IIC_MASTER_RX_EN && !IIC_MASTER_TX_EN)
  #error "at least one of IIC_SLAVEMODE_EN/IIC_MASTER_RX_EN/IIC_MASTER_TX_EN should be TRUE"
#endif 

#endif  // of IIC_H

源文件:

/*
*******************************************************************************************
*
*
*                                  IIC SUPPORT PACKAGE
*                                Freescale MC9S12XEP100
*                           飛思卡爾   MC9S12XEP100  IIC支持包
*
* File : IIC.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/05/15
* version: V1.2
* 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
* 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.
*           4. important !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*              comment the "interrupt" before the declaration of IIC_ISR, if you don't use 
*              IIC_ISR as ISR directly.
*              for example, if you use the IIC_uC_ISR in IIC_uCos.s as ISR.
*********************************************************************************************
*/


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

#include <stddef.h>
#include "IIC.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
/*
*********************************************************************************************************
*                                  LOCAL FUNCTION DECLARATION
*********************************************************************************************************
*/
// 發起啓動條件,默認當前爲從機模式,如總線忙則會返回錯誤,後面需要軟件查看IBIF來看是否成功
// CalAddr:主叫地址(D0:R/W)
static unsigned char _IIC_StartCondtion(unsigned char CalAddr);

// 默認的等待函數,無限等待
static unsigned char _IIC_Wait(unsigned long 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
#if(IIC_SLAVEMODE_EN == TRUE)
  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();
  if(err = _IIC_StartCondtion((calAddr << 1) | 0x01)) // 產生啓動信號併發送主叫地址+讀指令(bit 0 == 1)
    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();
  if(err = _IIC_StartCondtion(calAddr << 1)) // 產生啓動信號併發送主叫地址+寫指令(bit 0 == 0)
    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){
  m_assert(RxCnt > 0,"IIC錯誤,RxCnt == 0。\r\n"); // 這裏不應該出現0,出現了說明程序有bug
  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置位
#if(IIC_MASTER_TX_EN == TRUE)
      _IIC_ISR_MasterTx();   // 則爲主機發送中斷
#else
      m_assert(FALSE,"IIC錯誤,發生主機發送中斷");
#endif
    }else{     // 否則
#if(IIC_MASTER_RX_EN == TRUE)
      _IIC_ISR_MasterRx();   // 爲主機接收中斷
#else
      m_assert(FALSE,"IIC錯誤,發生主機接收中斷");
#endif
    }
  }else{                     // 如果當前爲從機
    if(IIC0_IBSR_IBAL){      // 如果發生仲裁丟失
      IIC0_IBSR_IBAL = 1;    // 清零標誌位
#if(IIC_MASTER_TX_EN || IIC_MASTER_RX_EN)
      _notifyRst(ISR_ERR_IBAL); // 通知仲裁丟失
      if(IIC0_IBSR_IAAS == 0) // 如果沒有被作爲從機尋址
        return;               // 則直接退出
#else
      m_assert(FALSE,"IIC錯誤,發生主機仲裁丟失中斷");
#endif
    }
#if(IIC_SLAVEMODE_EN == TRUE)
    if(IIC0_IBSR_IAAS){  // 如果被作爲從機尋址
      _IIC_ISR_Addressed();   // 爲地址匹配發生的中斷,(當前不支持10位地址)
      return;
    }
    if(IIC0_IBCR_TX_RX)
      _IIC_ISR_SlaveTx();
    else
      _IIC_ISR_SlaveRx();
#else
    m_assert(FALSE,"IIC錯誤,發生從機中斷。\r\n");
#endif
  }
}

#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/15
* version: V1.0
* History: 2018/05/15  V1.0   the prototype
* 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/15
* version: V1.0
* History: 2018/05/15  V1.0   the prototype
* 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.
*********************************************************************************************
*/

/*
*********************************************************************************************************
*                                       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.h中的IIC_SLAVEMODE_EN來設置當前程序爲從機/主機
// IIC_SLAVEMODE_EN == TRUE時當前爲從機程序

// 主從機的通訊使用的協議:
// 主機寫的時候第一個數據字節修改從機的寄存器指針的值,後面的數據字節則寫入從機寄存器
// 主機讀的時候則接收當前從機寄存器指針指向的字節
// 每次讀/寫後,從機的寄存器指針的值自增,增長到底後回到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();
#if(IIC_SLAVEMODE_EN == FALSE)
    // 從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 char prBuf[100];
void prinf(const char *str){
  strcpy(prBuf,str);
}

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(); 

就好了。
其他都差不多。

代碼下載

這裏把代碼及示例代碼提供打包下載。稍微收點分。
https://download.csdn.net/download/lin_strong/10416624

注意,下載的代碼中的裸奔程序直接運行時會跑飛,需要在void near IIC_ISR(void);前加一個interrupt才行。
暫時沒有找到能夠很方便地切換中斷聲明的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章