此为本人写的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,。