[嵌入式开发模块]MC9S12XEP100 IIC模块 驱动程序
此为本人写的MC9S12XEP100的IIC集成电路总线的硬件驱动程序。
文章目录
- 前言
- 相关理论
- 驱动模块简介
- 代码
- 驱动模块
- 基于UCOS-II的驱动
- 示例代码
- 代码下载
前言
相关理论
相关理论请自行参考数据手册。
此为我对数据手册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_DISABLEIIC0_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/Wwhile(!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 _DEBUGif(RxCnt == 0){dbgprintf("IIC Err,RxCnt == 0.\r\n"); // 这里不应该出现0,出现了说明程序有bug}
#endifif(_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_BANKEDvoid 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(); // 则为主机发送中断
#elsedbgprintf("IIC Err,发生主机发送中断.\r\n");
#endif}else{ // 否则
#ifndef IIC_MASTER_RX_DISABLE_IIC_ISR_MasterRx(); // 为主机接收中断
#elsedbgprintf("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; // 则直接退出
#elsedbgprintf("IIC Err,发生主机仲裁丢失中断.\r\n");
#endif}
#ifndef IIC_SLAVEMODE_DISABLEif(IIC0_IBSR_IAAS){ // 如果被作为从机寻址_IIC_ISR_Addressed(); // 为地址匹配发生的中断,(当前不支持10位地址)return;}if(IIC0_IBCR_TX_RX)_IIC_ISR_SlaveTx();else_IIC_ISR_SlaveRx();
#elsedbgprintf("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;elsereturn 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 OSIntExitxref OSIntNestingxref OSTCBCurxref _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 registerpsha ; Push GPAGE register onto current task's stackldaa EPAGE ; Get current value of EPAGE registerpsha ; Push EPAGE register onto current task's stackldaa RPAGE ; Get current value of RPAGE registerpsha ; Push RPAGE register onto current task's stackldaa PPAGE ; Get current value of PPAGE registerpsha ; Push PPAGE register onto current task's stackinc OSIntNesting ; Notify uC/OS-II about ISRldab OSIntNesting ; if (OSIntNesting == 1) {cmpb #$01bne IIC_uC_ISR1ldy OSTCBCur ; OSTCBCur->OSTCBStkPtr = Stack Pointersts 0,y ; }IIC_uC_ISR1:JSR _IIC_ISR ; near Call TxRx ISR handler. (See IIC.c); cli ; Optionally enable interrupts to allow interrupt nestingcall OSIntExit ; Notify uC/OS-II about end of ISR, a context switch may occur from within OSIntExit().pula ; Get value of PPAGE registerstaa PPAGE ; Store into CPU's PPAGE registerpula ; Get value of RPAGE registerstaa RPAGE ; Store into CPU's RPAGE registerpula ; Get value of EPAGE registerstaa EPAGE ; Store into CPU's EPAGE registerpula ; Get value of GPAGE registerstaa GPAGE ; Store into CPU's GPAGE registerrti ; 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 sysclkPLLCTL &= 0x8F; //DisaKble PLL circuitCRGINT &= 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=32MPLLCTL =PLLCTL|0x70; //Enable PLL circuitasm NOP;asm NOP;while(!(CRGFLG&0x08)); //PLLCLK is Locked alreadyCLKSEL |= 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;// 指针重新归0if((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,。
[嵌入式开发模块]MC9S12XEP100 IIC模块 驱动程序相关推荐
- [嵌入式开发模块]DS3231时钟芯片 驱动程序
刚刚写完DS3231时钟芯片的驱动程序.这里开源出来供大家使用.需要的自取. 先简单介绍下这个芯片,然后直接给驱动代码以及示例代码. DS3231简介 简介 DS3231是一款低成本.高精度I2C实时 ...
- c语言数组实现环形缓冲区,[嵌入式开发模块]环形缓冲区/循环队列 C语言实现
忙着毕设,很久没有写文章了,终于答辩完了,得了个校优秀毕业设计.毕设做的是个智能接口模块,用一周时间入门了,MC9S12XEP100的开发,又用一周时间入门了uC/OS-II嵌入式操作系统,在做毕设的 ...
- android驱动开发 老罗,在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序(老罗学习笔记3)...
在Android硬件抽象层(HAL)概要介绍和学习计划一文中,我们简要介绍了在Android系统为为硬件编写驱动程序的方法.简单来说,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的 ...
- MC9S12XEP100的IIC模块(IICV3)
最近在写DS3231时钟芯片的驱动,这个芯片使用IIC进行通讯,以前没有用过IIC模块,照着教材和示例程序写程序后发现各种问题.没办法,还是官方数据手册靠谱,遂把相应部分又翻译了一遍.果然发现示例程序 ...
- 基于Arduino IDE开发的LD3320语音识别模块
基于Arduino的LD3320语音识别模块设计详解 文章目录 基于Arduino的LD3320语音识别模块设计详解 前言 一.LD3320驱动编写 step 1.0 使用Arduino的SPI库,通 ...
- 《Windows CE嵌入式开发入门——基于Xscale架构》 第9章 Windows CE BSP及驱动程序结构分析
9.1 Windows CE驱动程序结构概述 Windows CE的驱动程序可以从多种角度进行区分. 1.从加载以及接口方式来区分 可以分为本机设备驱动(Built-In Driver).可加载驱动 ...
- 硬件开发——语音模块开发 (包含语音识别模块代码等资料包 )
一.语音模块以及硬件模块开发的行情 硬件模块的开发--常用:语音模块 语音模块STC11L08XE 比如 人脸识别打卡器--虹膜识别(购买方式) 语音识别(购买方式) 车牌识别(购买方式) 华为手机( ...
- DM368开发 -- 华为3G/4G模块移植
一.模块介绍 用的是华为 ME909s-821 Mini PCIe. 模块介绍,参看: ME909s-821 Mini PCIe 规格参数 其他模块,参看:华为全系列模块 产品介绍 ME909s-82 ...
- taro 引入js_Taro跨端开发之多业务模块管理 React Native篇(终篇)
React Native 热更新方案 rn的业务越来越庞大,同时协同的团队越来越多. rn的动态化就必须提上日程了. 对于rn热更新,首当其冲的问题就是分包. rn的基础库很大,再加上我们依赖了很多的 ...
最新文章
- 在NVIDIA A100 GPU中使用DALI和新的硬件JPEG解码器快速加载数据
- MATLAB实战系列(三十六)-MATLAB 离散Hopfield神经网络的分类——高校科研能力评价
- 例子 客户端_服务端也是可以主动向客户端推送数据的--WebSocket
- 论文学习12-Conditional Random Fields: Probabilistic Models for Segmenting and Labeling Sequence Data(CRF
- vc10的C2664和C2065错误
- 如何防范短信接口被恶意攻击
- Arduino 入门教程(十五) WS2811跑马灯
- easyui select 默认选中指定值
- 怎么安装mapinfo破解和符号库
- Android6.0源码下载
- Android开发之自定义DataTimePicker(日期时间选择器)
- zigbee芯片cc2430资料
- 炒鞋风潮下的“真鞋”鉴定生意
- Linux安装NVIDIA显卡驱动的正确姿势
- JavaScript - 通过居民身份证号码获取年龄和性别(函数封装)
- 创建XTP图表的方法
- java多项式加法与乘法_java多项式加减法
- 利用LSTM对脑电波信号进行分类
- java-大数据-精品课程目录(超级全)
- 投影html连接电脑,电脑如何链接投影仪_台式电脑主机怎么连接投影仪-win7之家...