📄 i2c.c
字号:
/************************************************** * * i2c.c * * CVS ID: $Id: i2c.c,v 1.11 2007/10/25 10:11:58 marcucci Exp $ * Author: Maurizio Marcucci [MM] - STM * Date: $Date: 2007/10/25 10:11:58 $ * Revision: $Revision: 1.11 $ * * Description: * * Low level I2C driver * *************************************************** * * COPYRIGHT (C) ST Microelectronics 2005 * All Rights Reserved * *************************************************** * * STM CVS Log: * * $Log: i2c.c,v $ * Revision 1.11 2007/10/25 10:11:58 marcucci * Enable SCL & SDA I2C lines only after complete I2C Hardware initilaization * * Revision 1.10 2007/10/12 13:17:42 marcucci * I2C interrupts procedure modifed to work at 400Khz and put a control to * avoid that Received bytes exceed the I2c buffer size * * Revision 1.9 2007/05/02 12:42:50 belardi * Fix for race condition between hostif and i2c driver that cause loss of host command reception in presence of frequent CRQ timeout * * Revision 1.8 2006/12/19 17:06:00 belardi * Sends 0x00 if host requests more bytes than available in current packet * * Revision 1.7 2006/10/18 12:38:44 belardi * Removed USE_STM_HOSTIF * * Revision 1.6 2006/09/18 09:55:23 belardi * Corrected CVS keyword usage * * Revision 1.5 2006/09/18 09:24:13 belardi * Added Log CVS keyword into file header * * ***************************************************/#include "gendef.h"#include "hwreg.h"#include "osal.h"#include "utility.h"#include "accordoptimer.h"#include "i2c.h"#include "player.h"#include "hostif_low.h"#include "hostif_high.h"#define RWBIT_MASK 0x01I2C_FLAGS_STRUCT i2c_flags;/* Link layer message buffer for send and receive functions */char i2c_irq_rx_buffer[DEFAULT_MAX_I2C_FRAME_SIZE];uint16 i2c_irq_rx_buffer_idx;char i2c_irq_tx_buffer[DEFAULT_MAX_I2C_FRAME_SIZE];uint16 i2c_irq_tx_buffer_idx;void i2c_event_isr(void);/******************************************************************************//* Function: i2c_soft_reset *//* *//*! \brief Initialize the I2C flags * \param void * \return void * \remark *//******************************************************************************/void i2c_soft_reset(void){ uint32 i; i2c_flags.i2c_rx_ready = 1; i2c_flags.i2c_new_cmd = 0; i2c_irq_rx_buffer_idx = 0; /* Initialize the buffers, only for debug */ for(i = 0; i < DEFAULT_MAX_I2C_FRAME_SIZE; i++) { i2c_irq_rx_buffer[i] = 0x00; } i2c_soft_reset_tx();}/******************************************************************************//* Function: i2c_soft_reset_tx *//* *//*! \brief Initialize the I2C flags fot tx direction only * \param void * \return void * \remark *//******************************************************************************/void i2c_soft_reset_tx(void){ uint32 i; i2c_flags.i2c_tx_ready = 1; i2c_flags.tx_msg_done = 1; i2c_irq_tx_buffer_idx = 0; /* Initialize the buffers, only for debug */ for(i = 0; i < DEFAULT_MAX_I2C_FRAME_SIZE; i++) { i2c_irq_tx_buffer[i] = 0x00; } /* Set I2C CRQ line to inactive */ PDB |= I2C_CRQ_OR_MASK;}/******************************************************************************//* Function: i2c_init *//* *//*! \brief Initialize the I2C hardware * \param void * \return void * \remark *//******************************************************************************/void i2c_init(void){ uint32 dummy; i2c_soft_reset(); I2C0_CR.all = 0; /* Configure Frequency Bits to match I2C setup/hold times */ I2C0_OAR2.field.fr = 6; // Input Frequency 66-80Mhz// I2C0_CCR.field.fm_sm = 0; // Standard I2C // I2C0_CCR.field.cc_0_6 = 0x32; // 10Khz I2C// I2C0_ECCR = 0x1A; /* Clock Control Register */ I2C0_CCR.field.fm_sm = 1; // Fast I2C I2C0_CCR.field.cc_0_6 = 0x6E; // 100Khz I2C 0xde = 100Khz // 200Khz I2C 0x6E = 200Khz /* Extended Clock Control Register */ /* set the clock divider along with I2C_CCR register */ I2C0_ECCR = 0x00; /* Peripheral Enable */ /* Repeated write to pe bit is required! */ I2C0_CR.field.pe = 1; I2C0_CR.field.pe = 1; /* Enable General Call (I2C address 0x00) */ /* This is not requires by CIS, should we delete it? */ I2C0_CR.field.engc = 1; I2C0_CR.field.ack = 1; /* Own Address Register*/ I2C0_OAR1 = 0; I2C0_OAR1 = I2C_SLAVE_ADDRESS; /* High bits of Own Address - used for 10 bit addressing */ I2C0_OAR2.field.add_8_9 = 0; OSAL_isr_install(OSAL_ISR_I2C0_EVENT, 0x0f, i2c_event_isr);// [RB] use the same handler for both interupt sources// [RB] This is not an error, see remarks in i2c_event_isr() OSAL_isr_install(OSAL_ISR_I2C0_DATA, 0x0f, i2c_event_isr); /* wait for I2C bus to be idle (why?) */ //while(I2C0_SR1.field.busy) dummy = I2C0_SR1.all; dummy = I2C0_SR2.all; /* Interrupt Enable */ I2C0_CR.field.ite = 1; //configure_gpio(PORT_B, GPIO_2, GPIO_AF_PP); // use GPIO_AF_PP for I2C SCL //configure_gpio(PORT_B, GPIO_3, GPIO_AF_PP); // use GPIO_AF_PP for I2C SDA // Do I2C Cell Initialization with SDA and SCL pins not connected to the I2C // Hardware cell. // Connect the SDA and SCL pin to the I2C Hardware cell only after its complete // initialization DISABLE_INTERRUPTS(); PC0B |= 0x000C; PC1B |= 0x000C; PC2B |= 0x000C; ENABLE_INTERRUPTS()}/******************************************************************************//* Function: i2c_event_isr *//* *//*! \brief I2C interrupt handler, event * \param void * \return void * \remark This version works around a hardware 'feature': sometimes the * "i2c byte transfer" interrupt on the last byte of a tx happens before * the "i2c event" interrupt. As a consequence, the Ack Failure condition * is not processed correctly and the Accordo+ does not release the * SDA line. * The workaround is to process both interrupt sources in the same * handler, to make sure that the AF condition is checked before * the byte transfer isntruction that resets the SDA line. * Since OS20 enables irq during interrupt processing, it is necessary * to disable interrupts in this handler, because it is not reentrant. *//******************************************************************************/void i2c_event_isr(void){ I2C_SR1_UNION i2c_sr1; I2C_SR2_UNION i2c_sr2; uint8 i2c_slave_addr; uint8 i2c_dummy_reg; DISABLE_INTERRUPTS(); i2c_sr1.all = I2C0_SR1.all; i2c_sr2.all = I2C0_SR2.all; //[MM]: In case of Slave Transmitter, Master Send NACK after reciving // Last Data Byte. It could happen that Stop interrupts was lost because // stop flag was set in I2C0_SR2 register but I2C_SR1.evf flag was not set. //if(i2c_sr1.field.evf) //{ if(i2c_sr1.field.adsl) { start_timer(HOSTIF_LL_STOP_TIMER, HOSTIF_LL_STOP_TIMER_DURATION); // Start Condition // Good Slave Address Detected // Reset I2C Flags. i2c_flags.af = 0; if(i2c_flags.i2c_tx_ready == 0) { // Start received while I2C Transmitter is active. // It is Stop Condition // Repeated Start Condition i2c_flags.i2c_tx_ready = 1; /* [RB] signal to application that the tx buffer is ready */ i2c_flags.tx_msg_done = 1; /* release the CRQ line (set high) */ PDB |= I2C_CRQ_OR_MASK; stop_timer(HOSTIF_LL_CRQ_TIMER); hostif_inevent |= HOSTIF_EVT_LL_TX_COMPLETE; OSAL_wake_thread(OSAL_THREAD_HostIfTask); } if(i2c_flags.i2c_rx_ready == 0) { // Start received while I2C Receiver is active. // It is Stop Condition // Repeated Start Condition i2c_flags.i2c_rx_ready = 1; hostif_inevent |= HOSTIF_EVT_LL_NEW_MSG; OSAL_wake_thread(OSAL_THREAD_HostIfTask); } /* First data byte after I2C Start condition */ /* is the address, read to check if it is a read or write */ i2c_slave_addr = I2C0_DR; if(i2c_slave_addr & RWBIT_MASK) { // Slave Transmitter (Read Condition) i2c_irq_tx_buffer_idx = 0; i2c_flags.i2c_tx_ready = 0; /* release the CRQ line (set high) */ PDB |= I2C_CRQ_OR_MASK; stop_timer(HOSTIF_LL_CRQ_TIMER); } else { // Slave Receiver (Write Condition) i2c_irq_rx_buffer_idx = 0; i2c_flags.i2c_rx_ready = 0; } }//if(i2c_sr1.field.adsl) else if (i2c_sr2.field.af == 1) { // Acknowledgement Failure i2c_flags.af = 1; //[MM]: As I2C specification Master send NACK and then it sent STOP. // Master can decide to read a quantity of data different from // what the Message Length says. This is not an error. //if (i2c_irq_tx_buffer_idx != i2c_irq_tx_buffer[0] + 1) //{ // //hostif_inevent |= HOSTIF_EVT_LL_AF_ERR; // //OSAL_wake_thread(OSAL_THREAD_HostIfTask); //} } else { if (i2c_sr2.field.stopf) { stop_timer(HOSTIF_LL_STOP_TIMER); // Stop Condition Detected if(i2c_flags.i2c_rx_ready == 0) { // Message Reception On Going i2c_flags.i2c_rx_ready = 1; // Release Receiver hostif_inevent |= HOSTIF_EVT_LL_NEW_MSG; OSAL_wake_thread(OSAL_THREAD_HostIfTask); } if(i2c_flags.i2c_tx_ready == 0) { i2c_flags.i2c_tx_ready = 1; /* [RB] signal to application that the tx buffer is ready */ i2c_flags.tx_msg_done = 1; hostif_inevent |= HOSTIF_EVT_LL_TX_COMPLETE; OSAL_wake_thread(OSAL_THREAD_HostIfTask); } } } //} /* The i2c_xfer_isr interrupt handler began here */ /* Byte Transfer Finished */ if(i2c_sr1.field.btf == 1) { /* TRA field provides info on the direction of the transfer */ if(i2c_sr1.field.tra == 1) { // Send Bytes if(i2c_flags.af == 0) { if(i2c_irq_tx_buffer_idx < i2c_irq_tx_buffer[0] + 1) { I2C0_DR = i2c_irq_tx_buffer[i2c_irq_tx_buffer_idx]; i2c_irq_tx_buffer_idx++; } else { I2C0_DR = 0x00; } } else { /* Acknowledge Failure error condition: release the SDA lines */ /* See datasheet Page 220 */ I2C0_DR = 0xFF; } } else { // [MM] Discard Data exceeding the size of I2C reciving Buffer if(i2c_irq_rx_buffer_idx < DEFAULT_MAX_I2C_FRAME_SIZE) { i2c_irq_rx_buffer[i2c_irq_rx_buffer_idx++] = I2C0_DR; } else { i2c_dummy_reg = I2C0_DR; } } } ENABLE_INTERRUPTS();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -