今天试一下PIC-IoT WA开发板上面的I2C,用它来驱动MCP9808温度传感器。
在之前的例子上,我们用MCC在配置一个I2C2,因为硬件用的就是I2C2。
这里MCC配置,增加了i2c2外设,速度400卡,引脚自动关联:
I2C2的中断只开了主机模式的中断:
我们来看看MCP9808的datasheet是如何描述的,仅列出几个关键点:
作为从机,地址是如何设定的:
这样我们来计算一下:板子电路上A0A1A2均为0,b'0011000=0x18,
跟原理图上给出的一致。
地址发送的具体时序图:
设置中断触发引脚的时序,我们可以不用:
读取寄存器数值的时序:
温度的具体转换公式:
读取器件的ID号:
其他功能还有设置上下限温度,可与中断引脚配合使用。
然后发现datasheet里面竟然提供了参考代码,MCP9808也是microchip的,
做的实在太详细,考虑的很周到。
下面进行代码的编写:
我们这里面仅有MCC生成i2c2代码的i2c2的初始化函数,其他的一律不用:
因为它的读写函数整合在一起了,我喜欢每个步骤独立的,这样用起来比较灵活方便。
控制代码参照官方的综合程序:
我们写i2c控制的每个步骤:
inline void i2c2_driver_close(void)
{
I2C2CONLbits.I2CEN = 0;
}
/* Interrupt Control */
inline void i2c2_enableIRQ(void)
{
IEC3bits.MI2C2IE = 1;
IEC3bits.SI2C2IE = 1;
}
inline bool i2c2_IRQisEnabled(void)
{
return IEC3bits.MI2C2IE || IEC3bits.SI2C2IE;
}
inline void i2c2_disableIRQ(void)
{
IEC3bits.MI2C2IE = 0;
IEC3bits.SI2C2IE = 0;
}
inline void i2c2_clearIRQ(void)
{
IFS3bits.MI2C2IF = 0;
IFS3bits.SI2C2IF = 0;
}
inline void i2c2_setIRQ(void)
{
IFS3bits.MI2C2IF = 1;
IFS3bits.SI2C2IF = 1;
}
inline void i2c2_waitForEvent(uint16_t *timeout)
{
//uint16_t to = (timeout!=NULL)?*timeout:100;
//to <<= 8;
if((IFS3bits.MI2C2IF == 0) && (IFS3bits.SI2C2IF == 0))
{
while(1)// to--)
{
if(IFS3bits.MI2C2IF || IFS3bits.SI2C2IF) break;
__delay_us(100);
}
}
}
bool i2c2_driver_driver_open(void)
{
if(!I2C2CONLbits.I2CEN)
{
// initialize the hardware
// STAT Setting
I2C2STAT = 0x0;
// CON Setting
I2C2CONL = 0x8000;
// Baud Rate Generator Value: I2CBRG 100000;
I2C2BRG = 78;
return true;
}
else
return false;
}
bool i2c2_driver_initSlaveHardware(void)
{
if(!I2C2CONLbits.I2CEN)
{
I2C2CONHbits.AHEN = 1;
I2C2CONHbits.DHEN = 1;
I2C2CONLbits.STREN = 1;
I2C2CONLbits.I2CEN = 1;
return true;
}
return false;
}
inline void i2c2_driver_start(void)
{
I2C2CONLbits.SEN = 1;
}
inline void i2c2_driver_restart(void)
{
I2C2CONLbits.RSEN = 1;
}
inline void i2c2_driver_stop(void)
{
I2C2CONLbits.PEN = 1;
}
inline bool i2c2_driver_isNACK(void)
{
return I2C2STATbits.ACKSTAT;
}
inline void i2c2_driver_startRX(void)
{
I2C2CONLbits.RCEN = 1;
}
inline char i2c2_driver_getRXData(void)
{
return I2C2RCV;
}
inline void i2c2_driver_TXData(uint8_t d)
{
I2C2TRN = d;
}
inline void i2c2_driver_sendACK(void)
{
I2C2CONLbits.ACKDT = 0;
I2C2CONLbits.ACKEN = 1; // start the ACK/NACK
}
inline void i2c2_driver_sendNACK(void)
{
I2C2CONLbits.ACKDT = 1;
I2C2CONLbits.ACKEN = 1; // start the ACK/NACK
}
inline void i2c2_driver_releaseClock(void)
{
I2C2CONLbits.SCLREL = 1;
}
inline bool i2c2_driver_isBuferFull(void)
{
return I2C2STATbits.RBF || I2C2STATbits.TBF;
}
inline bool i2c2_driver_isStart(void)
{
return I2C2STATbits.S;
}
inline bool i2c2_driver_isAddress(void)
{
return !I2C2STATbits.D_A;
}
inline bool i2c2_driver_isStop(void)
{
return I2C2STATbits.P;
}
inline bool i2c2_driver_isData(void)
{
return I2C2STATbits.D_A;
}
inline bool i2c2_driver_isRead(void)
{
return I2C2STATbits.R_W;
}
inline void i2c2_driver_clearBusCollision(void)
{
I2C2STATbits.BCL = 0; // clear the bus collision.
}
inline void i2c2_driver_enableStartIRQ(void)
{
I2C2CONHbits.SCIE = 1;
}
inline void i2c2_driver_disableStartIRQ(void)
{
I2C2CONHbits.SCIE = 0;
}
inline void i2c2_driver_enableStopIRQ(void)
{
I2C2CONHbits.PCIE = 1;
}
inline void i2c2_driver_disableStopIRQ(void)
{
I2C2CONHbits.PCIE = 0;
}
inline void i2c2_driver_setBusCollisionISR(interruptHandler handler){
i2c2_driver_busCollisionISR = handler;
}
inline void i2c2_driver_setMasterI2cISR(interruptHandler handler){
i2c2_driver_Masteri2cISR = handler;
}
inline void i2c2_driver_setSlaveI2cISR(interruptHandler handler){
i2c2_driver_Slavei2cISR = handler;
}
void __attribute__ ( ( interrupt, no_auto_psv ) ) _MI2C2Interrupt ( void )
{
(*i2c2_driver_Masteri2cISR)();
}
void __attribute__ ( ( interrupt, no_auto_psv ) ) _SI2C2Interrupt ( void )
{
(*i2c2_driver_Slavei2cISR)();
}
//定义一个状态机的结构定义的组合,包含各种不同的状态:
// I2C STATES
typedef enum {
I2C_IDLE = 0,
I2C_SEND_ADR_READ,
I2C_SEND_ADR_WRITE,
I2C_TX,
I2C_RX,
I2C_RCEN,
I2C_TX_EMPTY,
I2C_SEND_RESTART_READ,
I2C_SEND_RESTART_WRITE,
I2C_SEND_RESTART,
I2C_SEND_STOP,
I2C_RX_DO_ACK,
I2C_RX_DO_NACK_STOP,
I2C_RX_DO_NACK_RESTART,
I2C_RESET,
I2C_ADDRESS_NACK
} i2c_fsm_states_t;
//定义一个中断反馈状态的组合体:
typedef enum
{
i2c_dataComplete = 0,
i2c_writeCollision,
i2c_addressNACK,
i2c_dataNACK,
i2c_timeOut,
i2c_NULL
} i2c_callbackIndex;
//定义一个具体状态的结构体
typedef struct
{
unsigned busy:1;
unsigned inUse:1;
unsigned bufferFree:1;
unsigned addressNACKCheck:1;
i2c_address_t address; /// The I2C Address
uint8_t *data_ptr; /// pointer to a data buffer
size_t data_length; /// Bytes in the data buffer
uint16_t time_out; /// I2C Timeout Counter between I2C Events.
uint16_t time_out_value; /// Reload value for the timeouts
i2c_fsm_states_t state; /// Driver State
i2c_error_t error;
i2c_callback callbackTable[6];
void *callbackPayload[6]; /// each callback can have a payload
} i2c_status_t;
//定义一个实例
i2c_status_t i2c_status = {0};
//具体通讯的流程和中断函数
void i2c_setDataCompleteCallback(i2c_callback cb, void *p)
{
setCallBack(i2c_dataComplete,cb,p);
}
void i2c_setWriteCollisionCallback(i2c_callback cb, void *p)
{
setCallBack(i2c_writeCollision,cb,p);
}
void i2c_setAddressNACKCallback(i2c_callback cb, void *p)
{
setCallBack(i2c_addressNACK,cb,p);
}
void i2c_setDataNACKCallback(i2c_callback cb, void *p)
{
setCallBack(i2c_dataNACK,cb,p);
}
void i2c_setTimeOutCallback(i2c_callback cb, void *p)
{
setCallBack(i2c_timeOut,cb,p);
}
i2c_error_t i2c_open(i2c_address_t address)
{
i2c_error_t ret = I2C_BUSY;
if(!i2c_status.inUse)
{
i2c_status.address = address;
i2c_status.busy = 0;
i2c_status.inUse = 1;
i2c_status.addressNACKCheck = 0;
i2c_status.state = I2C_RESET;
i2c_status.time_out_value = 500; // MCC should determine a reasonable starting value here.
i2c_status.bufferFree = 1;
// set all the call backs to a default of sending stop
i2c_status.callbackTable[i2c_dataComplete]=returnStop;
i2c_status.callbackPayload[i2c_dataComplete] = NULL;
i2c_status.callbackTable[i2c_writeCollision]=returnStop;
i2c_status.callbackPayload[i2c_writeCollision] = NULL;
i2c_status.callbackTable[i2c_addressNACK]=returnStop;
i2c_status.callbackPayload[i2c_addressNACK] = NULL;
i2c_status.callbackTable[i2c_dataNACK]=returnStop;
i2c_status.callbackPayload[i2c_dataNACK] = NULL;
i2c_status.callbackTable[i2c_timeOut]=returnReset;
i2c_status.callbackPayload[i2c_timeOut] = NULL;
i2c2_driver_driver_open();
i2c2_clearIRQ();
i2c2_driver_setBusCollisionISR(i2c_busCollisionISR);
i2c2_driver_setMasterI2cISR(i2c_ISR);
i2c2_driver_setSlaveI2cISR(i2c_ISR);
// comment the IRQ enable for a polled driver.
i2c2_enableIRQ();
ret = I2C_NOERR;
}
return ret;
}
void i2c_setAddress(i2c_address_t address)
{
i2c_status.address = address;
}
i2c_error_t i2c_close(void)
{
i2c_error_t ret = I2C_BUSY;
if(!i2c_status.busy)
{
i2c_status.inUse = 0;
// close it down
i2c_status.address = 0xff; // 8-bit address is invalid so this is FREE
i2c2_clearIRQ();
i2c2_disableIRQ();
ret = i2c_status.error;
}
return ret;
}
void i2c_setTimeOut(uint8_t to)
{
i2c2_disableIRQ();
i2c_status.time_out_value = to;
i2c2_enableIRQ();
}
void i2c_setBuffer(void *buffer, size_t bufferSize)
{
if(i2c_status.bufferFree)
{
i2c_status.data_ptr = buffer;
i2c_status.data_length = bufferSize;
i2c_status.bufferFree = false;
}
}
i2c_error_t i2c_masterOperation(bool read)
{
i2c_error_t ret = I2C_BUSY;
if(!i2c_status.busy)
{
i2c_status.busy = true;
ret = I2C_NOERR;
if(read)
{
i2c_status.state = I2C_SEND_ADR_READ;
}
else
{
i2c_status.state = I2C_SEND_ADR_WRITE;
}
i2c2_driver_start();
if(! i2c2_IRQisEnabled())
i2c_poller();
}
return ret;
}
i2c_error_t i2c_masterRead(void)
{
return i2c_masterOperation(true);
}
i2c_error_t i2c_masterWrite(void)
{
return i2c_masterOperation(false);
}
inline void i2c_poller(void)
{
while(i2c_status.busy)
{
i2c2_waitForEvent(NULL);
i2c_ISR();
}
}
static i2c_fsm_states_t do_I2C_RESET(void)
{
i2c2_driver_resetBus();
i2c_status.busy = false; // Bus Free
i2c_status.error = I2C_NOERR;
return I2C_RESET; // park the FSM on reset
}
static i2c_fsm_states_t do_I2C_IDLE(void)
{
i2c_status.busy = false; // Bus Free
i2c_status.error = I2C_NOERR;
return I2C_RESET; // park the FSM on reset
}
static i2c_fsm_states_t do_I2C_SEND_RESTART_READ(void)
{
i2c2_driver_restart();
return I2C_SEND_ADR_READ;
}
static i2c_fsm_states_t do_I2C_SEND_RESTART_WRITE(void)
{
i2c2_driver_restart();
return I2C_SEND_ADR_WRITE;
}
static i2c_fsm_states_t do_I2C_SEND_RESTART(void)
{
i2c2_driver_restart();
return I2C_SEND_ADR_READ;
}
static i2c_fsm_states_t do_I2C_SEND_STOP(void)
{
i2c2_driver_stop();
return I2C_IDLE;
}
static i2c_fsm_states_t do_I2C_SEND_ADR_READ(void)
{
i2c_status.addressNACKCheck = 1;
i2c2_driver_TXData(i2c_status.address << 1 | 1);
return I2C_RCEN;
}
static i2c_fsm_states_t do_I2C_SEND_ADR_WRITE(void)
{
i2c_status.addressNACKCheck = 1;
i2c2_driver_TXData(i2c_status.address << 1);
return I2C_TX;
}
static i2c_fsm_states_t do_I2C_RCEN(void)
{
i2c_status.addressNACKCheck = 0;
i2c2_driver_startRX();
return I2C_RX;
}
static i2c_fsm_states_t do_I2C_DO_ACK(void)
{
i2c2_driver_sendACK();
return I2C_RCEN;
}
static i2c_fsm_states_t do_I2C_DO_NACK_STOP(void)
{
i2c2_driver_sendNACK();
return I2C_SEND_STOP;
}
static i2c_fsm_states_t do_I2C_DO_NACK_RESTART(void)
{
i2c2_driver_sendNACK();
return I2C_SEND_RESTART;
}
static i2c_fsm_states_t do_I2C_DO_ADDRESS_NACK(void)
{
i2c_status.addressNACKCheck = 0;
i2c_status.error = I2C_FAIL;
switch(i2c_status.callbackTable[i2c_addressNACK](i2c_status.callbackPayload[i2c_addressNACK]))
{
case i2c_restart_read:
case i2c_restart_write:
return do_I2C_SEND_RESTART();
default:
return do_I2C_SEND_STOP();
}
}
static i2c_fsm_states_t do_I2C_TX(void)
{
if(i2c2_driver_isNACK())
{
switch(i2c_status.callbackTable[i2c_dataNACK](i2c_status.callbackPayload[i2c_dataNACK]))
{
case i2c_restart_read:
return do_I2C_SEND_RESTART_READ();
case i2c_restart_write:
return do_I2C_SEND_RESTART_WRITE();
default:
case i2c_continue:
case i2c_stop:
return do_I2C_SEND_STOP();
}
}
else
{
i2c_status.addressNACKCheck = 0;
i2c2_driver_TXData(*i2c_status.data_ptr++);
return (--i2c_status.data_length)?I2C_TX:I2C_TX_EMPTY;
}
}
static i2c_fsm_states_t do_I2C_RX(void)
{
*i2c_status.data_ptr++ = i2c2_driver_getRXData();
if(--i2c_status.data_length)
{
i2c2_driver_sendACK();
return I2C_RCEN;
}
else
{
i2c_status.bufferFree = true;
switch(i2c_status.callbackTable[i2c_dataComplete](i2c_status.callbackPayload[i2c_dataComplete]))
{
case i2c_restart_write:
case i2c_restart_read:
return do_I2C_DO_NACK_RESTART();
default:
case i2c_continue:
case i2c_stop:
return do_I2C_DO_NACK_STOP();
}
}
}
static i2c_fsm_states_t do_I2C_TX_EMPTY(void)
{
i2c_status.bufferFree = true;
switch(i2c_status.callbackTable[i2c_dataComplete](i2c_status.callbackPayload[i2c_dataComplete]))
{
case i2c_restart_read:
case i2c_restart_write:
return do_I2C_SEND_RESTART();
case i2c_continue:
i2c2_setIRQ();
return I2C_TX;
default:
case i2c_stop:
return do_I2C_SEND_STOP();
}
}
typedef i2c_fsm_states_t (stateHandlerFunction)(void);
const stateHandlerFunction *fsmStateTable[] = {
do_I2C_IDLE, //I2C_IDLE
do_I2C_SEND_ADR_READ, //I2C_SEND_ADR_READ
do_I2C_SEND_ADR_WRITE, //I2C_SEND_ADR_WRITE
do_I2C_TX, //I2C_TX
do_I2C_RX, //I2C_RX
do_I2C_RCEN, //I2C_RCEN
do_I2C_TX_EMPTY, //I2C_TX_EMPTY
do_I2C_SEND_RESTART_READ, //I2C_SEND_RESTART_READ
do_I2C_SEND_RESTART_WRITE, //I2C_SEND_RESTART_WRITE
do_I2C_SEND_RESTART, //I2C_SEND_RESTART
do_I2C_SEND_STOP, //I2C_SEND_STOP
do_I2C_DO_ACK, //I2C_RX_DO_ACK
do_I2C_DO_NACK_STOP, //I2C_RX_DO_NACK_STOP
do_I2C_DO_NACK_RESTART, //I2C_RX_DO_NACK_RESTART
do_I2C_RESET, //I2C_RESET
do_I2C_DO_ADDRESS_NACK //I2C_ADDRESS_NACK
};
void i2c_ISR(void)
{
i2c2_clearIRQ();
// NOTE: We are ignoring the Write Collision flag.
// the write collision is when SSPBUF is written prematurely (2x in a row without sending)
// NACK After address override Exception handler
if(i2c_status.addressNACKCheck && i2c2_driver_isNACK())
{
i2c_status.state = I2C_ADDRESS_NACK; // State Override
}
i2c_status.state = fsmStateTable[i2c_status.state]();
}
void i2c_busCollisionISR(void)
{
i2c2_driver_clearBusCollision();
}
/************************************************************************/
/* Helper Functions */
/************************************************************************/
static i2c_operations_t returnStop(void *p)
{
return i2c_stop;
}
static i2c_operations_t returnReset(void *p)
{
return i2c_reset_link;
}
static void setCallBack(i2c_callbackIndex idx, i2c_callback cb, void *p)
{
if(cb)
{
i2c_status.callbackTable[idx] = cb;
i2c_status.callbackPayload[idx] = p;
}
else
{
i2c_status.callbackTable[idx] = returnStop;
i2c_status.callbackPayload[idx] = NULL;
}
}
//下面为封装好的顶层的I2C读写函数
static i2c_operations_t wr1RegCompleteHandler(void *p)
{
i2c_setBuffer(p,1);
i2c_setDataCompleteCallback(NULL,NULL);
return i2c_continue;
}
void i2c_write1ByteRegister(i2c_address_t address, uint8_t reg, uint8_t data)
{
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setDataCompleteCallback(wr1RegCompleteHandler,&data);
i2c_setBuffer(®,1);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == i2c_close()); // sit here until finished.
}
void i2c_writeNBytes(i2c_address_t address, void* data, size_t len)
{
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setBuffer(data,len);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == i2c_close()); // sit here until finished.
}
/****************************************************************/
static i2c_operations_t rd1RegCompleteHandler(void *p)
{
i2c_setBuffer(p,1);
i2c_setDataCompleteCallback(NULL,NULL);
return i2c_restart_read;
}
uint8_t i2c_read1ByteRegister(i2c_address_t address, uint8_t reg)
{
uint8_t d2=42;
i2c_error_t e;
int x;
for(x = 2; x != 0; x--)
{
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setDataCompleteCallback(rd1RegCompleteHandler,&d2);
i2c_setBuffer(®,1);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == (e = i2c_close())); // sit here until finished.
if(e==I2C_NOERR) break;
}
return d2;
}
/****************************************************************/
static i2c_operations_t rd2RegCompleteHandler(void *p)
{
i2c_setBuffer(p,2);
i2c_setDataCompleteCallback(NULL,NULL);
return i2c_restart_read;
}
uint16_t i2c_read2ByteRegister(i2c_address_t address, uint8_t reg)
{
// result is little endian
uint16_t result;
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setDataCompleteCallback(rd2RegCompleteHandler,&result);
i2c_setBuffer(®,1);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == i2c_close()); // sit here until finished.
return (result << 8 | result >> 8);
}
/****************************************************************/
static i2c_operations_t wr2RegCompleteHandler(void *p)
{
i2c_setBuffer(p,2);
i2c_setDataCompleteCallback(NULL,NULL);
return i2c_continue;
}
void i2c_write2ByteRegister(i2c_address_t address, uint8_t reg, uint16_t data)
{
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setDataCompleteCallback(wr2RegCompleteHandler,&data);
i2c_setBuffer(®,1);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == i2c_close()); // sit here until finished.
}
/****************************************************************/
typedef struct
{
size_t len;
char *data;
}buf_t;
static i2c_operations_t rdBlkRegCompleteHandler(void *p)
{
i2c_setBuffer(((buf_t *)p)->data,((buf_t*)p)->len);
i2c_setDataCompleteCallback(NULL,NULL);
return i2c_restart_read;
}
void i2c_readDataBlock(i2c_address_t address, uint8_t reg, void *data, size_t len)
{
// result is little endian
buf_t d;
d.data = data;
d.len = len;
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setDataCompleteCallback(rdBlkRegCompleteHandler,&d);
i2c_setBuffer(®,1);
i2c_setAddressNACKCallback(i2c_restartWrite,NULL); //NACK polling?
i2c_masterWrite();
while(I2C_BUSY == i2c_close()); // sit here until finished.
}
void i2c_readNBytes(i2c_address_t address, void *data, size_t len)
{
while(!i2c_open(address)); // sit here until we get the bus..
i2c_setBuffer(data,len);
i2c_masterRead();
while(I2C_BUSY == i2c_close()); // sit here until finished.
}
我们调用i2c_read2ByteRegister(i2c_address_t address, uint8_t reg)读2个字节的函数,
因为MC9808的数值就是2个字节,高字节在前。
计算的公式:
寄存器的具体定义:
//具体代码:
#define MCP9809_ADDR 0x18 //从地址
#define MCP9808_REG_TA 0x05 //TA地址
int16_t SENSORS_getTempValue (void)
{
int32_t temperature;
temperature = i2c_read2ByteRegister(MCP9809_ADDR, MCP9808_REG_TA);
temperature = temperature << 19;
temperature = temperature >> 19;
temperature *= 100;
temperature /= 16;
return temperature;
}
然后在main函数里面调用:
nowTemperature=SENSORS_getTempValue();
printf("the Temp is: %.02f ℃\r\n",nowTemperature/100.0);
编译下载,串口输出为:
看起来不错,0.2℃以内跳动。好了今天就到这。
|
|