此爲本人寫的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才行。
暫時沒有找到能夠很方便地切換中斷聲明的方法。