Part Number: EK-TM4C123GXL
Hello, I cannot make the I2C work properly to interface with the MCP4725 DAC. Any help is appreciated.
Here is my code:
unsigned long *RCGCI2C = (unsigned long*) 0x400FE620; //Base 0x400F.E000, offset 0x620
unsigned long *RCGCGPIO = (unsigned long*) 0x400FE608; // Base 0x400F.E000, offset 0x608
unsigned long *GPIOAFSEL = (unsigned long*) 0x40005420; //Base port B 0x4000.5000, offset 0x420
unsigned long *GPIODEN = (unsigned long*) 0x4000551C; //Base port B 0x4000.5000, offset 0x51C
unsigned long *GPIOODR = (unsigned long*) 0x4000550C; //Base port B, offset 0x50C
unsigned long *GPIOCTL = (unsigned long*) 0x4000552C; //base port B, offset 0x52C
unsigned long *GPIOPUR = (unsigned long*) 0x40005510; //base port B, offset 0x510
unsigned long *GPIOCR = (unsigned long*) 0x40005524; //base port B, offset 0x524
unsigned long *GPIOLOCK = (unsigned long*) 0x40005520; //base port B, offset 0x520
unsigned long *I2CMCR = (unsigned long*) 0x40020020; //base I2C0 0x4002.0000, offset 0x020
unsigned long *I2CMTPR = (unsigned long*) 0x4002000C; //base I2C0 0x4002.0000, offset 0x00C
unsigned long *I2CMSA = (unsigned long*) 0x40020000; //base i2c0, offset 0x000
unsigned long *I2CMDR = (unsigned long*) 0x40020008; //base i2c0, offset 0x008
unsigned long *I2CMCS = (unsigned long*) 0x40020004; //base i2c0, offset 0x004
void clock_setup() {
//RCC pg 254, base 0x400F E000, offset 0x060
//RCC2 pg 260, base 0x400F E000, offset 0x070
//Step 1
//Bypass the PLL and system clock divider by setting the bypass2 bit and clearing the usesys2
//bit, thereby configuring the microcontroller to run off a "raw" clock source" and allowing
//for the new pll configuration to be validated before switching the clock to the PLL
*rcc2 = *rcc2 | 1 << 31; // Set USERCC2 to 1, using the RCC2 register instead of the RCC register
*rcc = *rcc & 0xFFBFFFFF; //Clear USESYSDIV, set the 22nd bit of rcc to 0
*rcc2 = *rcc2 | 1 << 11; // Set BYPASS2 to 1, the 11th bit of RCC2 (overwrites rcc)
//Step 2
//Select the crystal value (XTAL) and oscillator source (OSCSRC), and clear
//the PRDWN bit in rcc2
*rcc = *rcc & 0xFFFFF83F; //clear the xtal values bit 10:6 in RCC register
*rcc = *rcc | 0x00000540; //set the xtal values to the equivalent of 0x15 in bits 10:6 for 16Mhz crystal
*rcc2 = *rcc2 & 0xFFFFFF8F; //select the mainosc of oscsrc2 in bits 6:4 of rcc2: 0x0 (also clears the bits)
*rcc2 = *rcc2 | 0x00000010; //set the oscillator to precision oscillator, 0x1
*rcc2 = *rcc2 & 0xFFFFDFFF; // Clear the PWRDWN2 by setting 0 to bit 13 in RCC2 to cause the PLL to operate normally
//Part 3 - choose speed
//*rcc = *rcc | 1 << 22; //set bit 22 of RCC to use system division
//Select Sysdiv2 in RCC2 bit 28:23 , 0x3 is for /4 = 50Mhz
*rcc2 = *rcc2 & 0xE07FFFFF ; //clear sysdiv2 in rcc2
//set pointers to memory locations at base + offset
//unsigned long *rcc = (unsigned long*) 0x400FE060;
//unsigned long *rcc2 = (unsigned long*) 0x400FE070;
//table 5-5 for selecting sysdiv2, from freq/1 to freq/64, denominator is (wanted division) - 1 in hex. for example:
//freq/10 = 0x09 (9 in hex)
//0x03 is 50Mhz
*rcc2 = *rcc2 | 0x03 << 23; //set the sysdiv2 to the appropriate speed, use the shift operator because its 5 bits long but only takes 4 bit divisors
//Step 4
//wait for PLL to lock
//wait for RIS register bit 6 to be 1 which indicates the PLL is locked
wait_for_pll();
//RCGCGPIO, pg 340, address 0x400FE000, offset 0x608, sends a clock to a specific port, bit 5 is port F
//set bit 5 to 1 to enable the clock to go to port f
unsigned long *RCGC = (unsigned long*) 0x400FE608;
*RCGC = *RCGC | 0x20;
}
void i2c_configure() {
// Instructions from pg. 1015 in data sheet, 16.4.1
// 1. Enable the I2C clock using the RCGCI2C register in the system control module on page 348
*RCGCI2C = *RCGCI2C | 1; //setting bit 0 to high enables I2C module 0
// 2. Enable the clock to the GPIO module via RCGCGPIO, pg 340. Refer to table 23-5 on page 1351 for which GPIO port to enable.
// I2C module 0 is on Port B, so we set bit 1 to high
*RCGCGPIO = *RCGCGPIO | (1<<1);
// 3. In the GPIO module, enable the appropriate pins for alternate function using GPIOAFSEL (page 671). Refer to table 23-4 page 1344
// for which GPIOs to configure
// pin 47 for i2c0 scl (pb2), pin 48 for i2c0 sda (pb3)
// also our DAC requires pull up resistors according to the data sheet
// the tiva requires modifying GPIOLOCK and GPIOCR to modify GPIOPUR
*GPIOLOCK = *GPIOLOCK | 0x4C4F434B; //the only value that can be written here from tiva data sheet
*GPIOCR = *GPIOCR | (1<<2) | (1<<3); //allow changing of bits 2 and 3 in other registers
*GPIOAFSEL = *GPIOAFSEL | (1<<2) | (1<<3); // alternate function select for pins 2 and 3
*GPIODEN = *GPIODEN | (1<<2) | (1<<3); // enable digital output for pins 2 and 3
*GPIOPUR = *GPIOPUR | (1<<2) | (1<<3); //enable pull up resistors on pins 2 and 3
// 4. Enable the I2CSDA pin for open-drain operation, see page 676
*GPIOODR = *GPIOODR | (1<<3); //set the i2C data pin (pb3) of GPIO port B to open drain
// 5. Config the PMCn fields in the GPIOCTL reg to assign the I2C signal to the appropriate pins, see page 688 and table 23-5 on page 1351
// table 23-5 says I need to put a 3 in GPIOCTL pmc for pin 2 and pin 3
*GPIOCTL = *GPIOCTL | (3<<8) | (3<<12); //mux gpio to i2c by setting a 3 in pmc2 and pmc3
// 6. Initialize I2C Master by writing the I2CMCR reg with value of 0x000.0010
*I2CMCR = *I2CMCR | (1<<4); //initializes master function
// 7. Set the desired SCL clock speed of 100 Kbps by writing the I2CMTPR (i2c master time period) reg with value.
// can choose 3 speesds, choose 100K arbitrarily
// TPR = (sysclk/ (2*(SCL_LP + SCL_HP)*SCL_CLK))-1
// TPR = (50MHz/(2*(6+4)*100000))-1
/****************************************************
//assuming system clock is 50Mhz, may need to change
***************************************************/
// TPR = 24
// write the I2CMTPR reg with value of 0x0000.0024
*I2CMTPR = *I2CMTPR | (0x24<<0);
// 8. Specify the slave address of the master and that the next op is transmit by writing I2CMSA
// i2cmsa bits 7-1 are the slave address, the mcp4725 chip slave address is (not sure), or 0x62 is internet guides
// we also need to set next operation to transmit, by setting bit 0 (low for transmit, high for receive)
// therefore need to write (not sure)
// this is sent automatically each time as the first byte
// 9. Place data(byte) to be transmitted in the data reg by writing the I2C Master Data register (I2CMDR)
// reg with the data, 1 byte in length
// The device takes 4 bytes of data, and transmits an Acknowledge bit after receiving each byte
//
// Byte 1 Byte 2 Byte 3 Byte 4
// S |7|6|5|4|3|2|1|0| A |7|6|5|4|3|2|1|0| A |7|6|5|4|3|2|1|0| A |7|6|5|4|3|2|1|0| A
// | Dev |Addr |R| |C2-C0| x |PDs|x| | 12 Bit data tx |x x x x|
// | Code |Bits |W| | D11 - D0 |
//
//S: start bit sent by master
//A: received bit set by slave after each byte
//Dev Code: Slave Address, 1100 by default
//Address Bits: A2, A1, A0. A2/A1 are 0 by default, A0 is user defined
//R/W: what action peforming on slave register
//C2/C1/C0: what type of read/write operation -- '010' for write to DAC register
//PDs: set both to 0 for normal operation
//Dn: transmit data, see below for how the output voltage is affected
// Vdd * Dn
// Vout = ------------
// 4096
// 10. Initiate byte 1 & 2 transmit of the data from Master to Slave by writing the I2CMCS reg
// (STOP, START, RUN), use the "write only control register" bit map
// bit 0 RUN
// bit 1 START
// bit 2 StOP, if this bit is not high, will transmit continually
// 11. Wait until the transmission completes by polling the I2CMCS reg's BUSBSY bit until it has been cleared
// i2cmcs busbsy is bit 6
// value of 1 indicates i2c still busy
// 12. Check the error bit in the I2CMCS reg to confirm the transmit was ackonwledged
// transmitted MSB first
//set slave address
*I2CMSA = *I2CMSA | 0xC0; // device address is 1100, plus the An bits = 000, adding the R/W bit makes 0x1100.0000
//byte 1 & 2
*I2CMDR = *I2CMDR | 0x40;
while ( (*I2CMCS & 1<<6) != 0);
*I2CMCS = *I2CMCS | (1<<1) | (1<<0); //Start & Run
while ( (*I2CMCS & 1<<0) != 0);
//**************error check
if ( (*I2CMCS & 1<<1) != 0) { //check for error
*I2CMCS = *I2CMCS | (1<<2); //send stop if error
while ( (*I2CMCS & 1<<0) != 0);
}
*I2CMCS = *I2CMCS | (1<<0); //Run
//byte 3
while ( (*I2CMCS & 1<<0) != 0);
//**************error check
if ( (*I2CMCS & 1<<1) != 0) { //check for error
*I2CMCS = *I2CMCS | (1<<2); //send stop if error
while ( (*I2CMCS & 1<<0) != 0);
}
//**************
*I2CMDR = *I2CMDR | (0x00); //set output voltage
*I2CMCS = *I2CMCS | (1<<0); //send run bit
//byte 4
while ( (*I2CMCS & 1<<0) != 0);
//**************error check
if ( (*I2CMCS & 1<<1) != 0) { //check for error
*I2CMCS = *I2CMCS | (1<<2); //send stop
while ( (*I2CMCS & 1<<0) != 0);
}
//**************error check
*I2CMDR = *I2CMDR | (0x00); //set output voltage
*I2CMCS = *I2CMCS | (1<<0); //send Stop and Run
while ( (*I2CMCS & 1<<0) != 0);
//**************
if ( (*I2CMCS & 1<<1) != 0) { //check for error
*I2CMCS = *I2CMCS | (1<<2); //send stop
while ( (*I2CMCS & 1<<0) != 0);
}
//**************
}
int main() {
clock_setup();
i2c_configure();
}