Hi there,
I am sending two bytes of data (0x10 and 0x00) from the Master in burst mode over I2C0.
The following code works fine as long as I insert two calls to SysCtlDelay prior to placing data onto the I2C master bus and sending it (burst mode). I am able to observe the data being written using an Agilent DSO that has an I2C decoding option.
Why do I need these delays ? Without it (or without placing a BP at the line after it) the first byte (0x10 in this case) is NOT transmitted only the second and subsequent bytes are.
Jump down to the function named I2C_start_measuring to see where the calls to ROM_SysCtlDelay are.
void configure_I2C(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
// I2C0 is used with PortB[3:2].
// GPIO port B needs to be enabled so these pins can be used.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
//
// Configure the pin muxing for I2C0 functions on port B2 and B3.
// This is labelled PB2 and PB3 on the eval board. (PB=PortB)
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
// set PB3 as I2C SDA pin. the function name omits the word SDA and one needs to read the documentation to discover this.
// note an earlier version of my code had both pins being set to SDA (when only one should have) then one of the both being set to SCL
//This call sets the pin/pad to open-drain operation with weak pull up as per p. 1277 of the datasheet
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
//Configure PB2 pin for use as SCL by the I2C peripheral. this sets the pad for push-pull operation (not open drain (OD) as per as per p. 1277 of the datasheet).
//OD is is usually how I2C works
//see here for the confusion surrounding this being set to push pull rather than open drain http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/254099.aspx
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE,GPIO_PIN_2);
//
// (En/dis)able loopback mode as required. Loopback mode is a built in feature that is useful for debugging I2C operations. It internally connects the I2C
// master and slave terminals, which effectively let's you send data as a master and receive data as a slave.
//Refer to p. 1057 of E:\Documentation\Device\Datasheet-LM4F232H5QD.pdf NOTE: For external I2C operation you will need to use external pullups
// that are stronger than the internal pullups. Refer to the datasheet for more information.
//temporarily disable loopback by explicitly setting bit 0 of I2C_O_MCR to 0 (does this code actually work ?!) and commenting out the next line
//which sets it to 1
HWREG(I2C0_BASE + I2C_O_MCR) |= 0x00;
//HWREG(I2C0_BASE + I2C_O_MCR) |= 0x01;
//Sensor might use a clock rate of 400kbps , this isn't documented anywhere I could find, even in their example code.
// so set the last parameter to true for now as If the parameter bFast is true, then the master block is set up to transfer data at 400 Kbps;
// otherwise, it is set up to transfer data at 100 Kbps
I2CMasterInitExpClk(I2C0_BASE, ui32SysClock /*SysCtlClockGet()*/, true);
I2CMasterSlaveAddrSet(I2C0_BASE, SENS1_AIRPATH_FLOW_SLAVE_ADDRESS, false /*true*/); //false=>Master reads from slave however the first command will be a write !! (it doesn't work for this parameter to be true)
//wait until the Master is not busy sending
while(I2CMasterBusy(I2C0_BASE))
{
}
I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
{
UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
}
}
void I2C_start_measuring(void)
{
ROM_SysCtlDelay(150);//without a delay of at least 80 (at 100MHz) the first byte is not decoded by the CRO.
I2CMasterDataPut(I2C0_BASE, 0x10);//Tx first 8 bits of 0x1000. sometimes this isn't properly written or decoded by the CRO. Why ?
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);// Initiate send of character from Master to Slave
while(I2CMasterBusy(I2C0_BASE))
{
//wait until bus is not busy
}
//Check for and report on any error
I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
{
UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
}
ROM_SysCtlDelay(150);//without a delay of at least 80 (at 100MHz) the first byte is not decoded by the CRO.
I2CMasterDataPut(I2C0_BASE, 0x00);//Tx second 8 bits of 0x1000
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);// Initiate send of character from Master to Slave
while(I2CMasterBusy(I2C0_BASE))
{
//wait until bus is not busy
}
//Check for and report on any error
I2CMasterErrorValue=I2CMasterErr(I2C0_BASE);
if (I2CMasterErrorValue!=I2C_MASTER_ERR_NONE)
{
UARTprintf("\nI2C Master Error: %d\n", I2CMasterErrorValue);
}
}
int main(void)
{
uint32_t TxData[NUMBER_OF_SAMPLES_PER_CHANNEL]; //32 bits of data in each array element however SPI only uses 16 bits
uint32_t RxData[NUMBER_OF_SAMPLES_PER_CHANNEL]; //32 bits of data in each array element however SPI only uses 16 bits
uint32_t index,mainLoopIndex,SysTickPeriod;//SysTickPeriod is the period to set the SysTick timer.
uint32_t CPU_Load,SysTickAfterAllTasksRun=0;//CPU_Load is the CPU load as an integer value (units=%).
//SysTickAfterAllTasksRun it the value the Systick timer has counted down
//to after all tasks in the main loop have run.
// uint32_t ui32SysClock; // system clock setting schieved by SysCtlClockFreqSet now extern
unsigned short TxDataRaw;//SPI bus is only 16 bits wide
long ReadDataCount=0;// number of Data bytes read from the Rx read FIFO buffer
long WriteDataCount=0;// number of Data bytes able to be written to the Tx write FIFO buffer
uint32_t ulDataRx[NUM_SSI_DATA]; //DataRx is an array to receive the data from the AD7606. Each element contains 32 bits of data.
//
// Set the clocking to run directly from the external crystal/oscillator.
// TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
// crystal on your board.
// use the following line to run the system clock at 16MHz directly from the external crystal
// SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
// set the Master clock to be say 50MHz (max is 80MHz) by
// running from the external crystal via the PLL. The PLL runs at 200MHz
// Divide the 200MHz signal by 4.0 (SYSCTL_SYSDIV_4).
// The main crystal on the TI LM4F eval board is 16MHz
// set the Master clock to be 50MHz which is a period of 20ns
// SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); //deprecated
// On TM4C129 the function pair of SysCtlClockSet and SysCtlClockGet are now replaced by
// SysCtlClockFreqSet which returns the System Clock
// set the Master clock to be 100MHz which is a period of 10ns (Max speed is 120MHz and is avoided for now to leave a buffer for future use if needed)
ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
SYSCTL_CFG_VCO_480), 100000000);
// to run at 80MHz use SYSCTL_SYSDIV_2_5
//SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//setup the SysTick timer used to delay until the next time multiple
//this method is only being used until a real ADC value is being returned and we can sync off that
//If the master clock is set to run at 16MHz (max 80MHz)
//then loading 16,000,000 into the systick period will cause it to wrap around once per second
//it is a 24 bit counter capable of counting down from 2^24=16,777,216
//SysTickPeriod=(SysCtlClockGet()*INTER_PROCESSOR_SAMPLE_PERIOD_IN_MILLISECONDS)/1000;//be careful of the order of evaluation interim fractional results cause truncation.incorrect values
SysTickPeriod=(ui32SysClock*INTER_PROCESSOR_SAMPLE_PERIOD_IN_MILLISECONDS)/1000;//be careful of the order of evaluation interim fractional results cause truncation.incorrect values
//i.e. SysTickPeriod=50,000,000 x 50 / 1000 = 2,500,000 for 20Hz bulk sample frequency
// or SysTickPeriod=50,000,000 x 20 / 1000 = 1,000,000 for 50Hz bulk sample frequency
SysTickPeriodSet(SysTickPeriod);
//this sets the SysTickPeriod to be the number of system clock periods during the BULK_SAMPLE_PERIOD
//setting the SysTickPeriod to this means the counter will wrap around that often.
//If not using the ADC for timing then this could be used as the interrupt period.
//when faster than 16MHz the SysTick counter will wrap around in less than 1 second as it only has 16,777,216 values
//at 50MHz the Systick counter will reach
// Enable SysTick to generate interrupts.
SysTickIntEnable();
//the systick counter will wrap around in 1 second at 16MHz, and proportionately shorter at the faster clock speeds.
//for this reason when timing, ensure you don't exceed the period of the systick timer
//reset the timer to a known value by writing to the NVIC_ST__CURRENT register.
//refer to the Stellaris Peripheral Driver Library UG and search for NVIC_ST_CURRENT
//this might not be necessary as I think the act of loading a value in sets it to zero.
HWREG(NVIC_ST_CURRENT)=0x000000;
//the SysTick timer is enabled later on.
//
// Set up the serial console to use for displaying messages. This is
// just for debugging and is not needed for SPI operation.
//
InitConsole();
//init the I2C
configure_I2C();
//send start measurement command
I2C_start_measuring();
//read flow measurement data
read_I2C_data();
}