/* This program writes and reads an I2C device on the Wytec EduPad board. * * The Wytec EduPad board has a 24LC02B Serial EEPROM. This program writes * ramdom data to the memory and read back to verify. Both data written and * read back are displayed on the console (UART0 at the virtual COM port). * If there is a mismatch, five asterisks are printer. * Both single byte write/read and page write/read are tested. * * Built and tested with Keil MDK-ARM v5.24a and TM4C_DFP v1.1.0 */ #include "TM4C123GH6PM.h" #include #include void delayMs(int n); void UART_init(int baud); void I2C1_init(void); int I2C1_byteWrite(int slaveAddr, unsigned char memAddr, const unsigned char data); int I2C1_pageWrite(int slaveAddr, unsigned char memAddr, int byteCount, unsigned char* data); int I2C1_byteRead(int slaveAddr, unsigned char memAddr, unsigned char* data); int I2C1_burstRead(int slaveAddr, unsigned char memAddr, int byteCount, unsigned char* data); void I2C1_ackPoll(int slaveAddr); #define SLAVE_ADDR 0x53 // 1010 011. #define SIZE 256 int main(void) { unsigned char readback[SIZE]; int error; unsigned char testData[SIZE]; int i; UART_init(115200); // call subroutine to init uart lib i = *((int*)0x20001540); i = 0; printf("\r\n24LC02B Serial EEPROM Test %d\r\n", (unsigned int)i); srand(i); for (i = 0; i < SIZE; i++) testData[i] = rand() & 0xff; I2C1_init(); for (i = 0; i < SIZE; i++) { error = I2C1_byteWrite(SLAVE_ADDR, i, testData[i]); if (error) for(;;) ; // replace with error handling I2C1_ackPoll(SLAVE_ADDR); // wait until write completed printf("."); delayMs(1); } for (i = 0; i < SIZE; i++) { error = I2C1_byteRead(SLAVE_ADDR, i, &readback[i]); if (error) for(;;) ; // replace with error handling } for (i = 0; i < SIZE; i++) { printf("\r\n%2X: %2X - %2X %s", i, testData[i], readback[i], (testData[i] == readback[i]) ? " " : "***"); } printf("\r\n\r\n24LC02B Serial EEPROM Page Write Test"); for (i = 0; i < SIZE; i++) testData[i] = rand() & 0xff; for (i = 0; i < (SIZE / 8); i++) { error = I2C1_pageWrite(SLAVE_ADDR, i * 8, 8, &testData[i * 8]); if (error) for(;;) ; // replace with error handling I2C1_ackPoll(SLAVE_ADDR); // wait until write completed printf("#"); delayMs(100); } error = I2C1_burstRead(SLAVE_ADDR, 0, 256, readback); if (error) for(;;) ; // replace with error handling for (i = 0; i < SIZE; i++) { printf("\r\n%2X: %2X - %2X %s", i, testData[i], readback[i], (testData[i] == readback[i]) ? " " : "***"); } for (;;) { } } void I2C1_init(void) { SYSCTL->RCGCI2C |= 0x02; // enable clock to I2C1 SYSCTL->RCGCGPIO |= 0x01; // enable clock to GPIOA // PORTA 7, 6 for I2C1 GPIOA->AFSEL |= 0xC0; // PORTA 7, 6 for I2C1 GPIOA->PCTL &= ~0xFF000000; // PORTA 7, 6 for I2C1 GPIOA->PCTL |= 0x33000000; GPIOA->DEN |= 0xC0; // PORTA 7, 6 as digital pins GPIOA->ODR |= 0x80; // PORTA 7 as open drain I2C1->MCR = 0x10; // master mode I2C1->MTPR = 49; // 100 kHz @ 50 MHz } static int I2C1_wait_till_done(void) { while(I2C1->MCS & 1); // wait until I2C1 master is not busy return I2C1->MCS & 0xE; // return I2C1 error code } // Write one byte int I2C1_byteWrite(int slaveAddr, unsigned char memAddr, const unsigned char data) { unsigned char error; // write a byte: S-(addr+w)-ACK-memAddr-ACK-data-ACK-P I2C1->MSA = slaveAddr << 1; I2C1->MDR = memAddr; I2C1->MCS = 3; // S-(addr+w)-ACK-memAddr-ACK error = I2C1_wait_till_done(); // wait until write is complete if (error) return error; I2C1->MDR = data; I2C1->MCS = 5; // -data-ACK-P error = I2C1_wait_till_done(); // wait until write is complete while(I2C1->MCS & 0x40); // wait until bus is not busy error = I2C1->MCS & 0xE; return error; } // Use burst write to write multiple bytes to consecutive locations // burst write: S-(saddr+w)-ACK-maddr-ACK-data-ACK-data-ACK-...-data-ACK-P int I2C1_pageWrite(int slaveAddr, unsigned char memAddr, int byteCount, unsigned char* data) { char error; if (byteCount <= 0) return -1; // no write was performed // send slave address and starting address I2C1->MSA = slaveAddr << 1; I2C1->MDR = memAddr; I2C1->MCS = 3; // S-(saddr+w)-ACK-maddr-ACK error = I2C1_wait_till_done(); // wait until write is complete if (error) return error; // send data one byte at a time while (byteCount > 1) { I2C1->MDR = *data++; // write the next byte I2C1->MCS = 1; // -data-ACK- error = I2C1_wait_till_done(); if (error) return error; byteCount--; } // send last byte and a STOP I2C1->MDR = *data++; // write the last byte I2C1->MCS = 5; // -data-ACK-P error = I2C1_wait_till_done(); while(I2C1->MCS & 0x40); // wait until bus is not busy if (error) return error; return 0; // no error } // Read one byte int I2C1_byteRead(int slaveAddr, unsigned char memAddr, unsigned char* data) { unsigned char error; // read: S-(addr+w)-ACK-memAddr-ACK-R-(addr+r)-ACK-data-NACK-P I2C1->MSA = slaveAddr << 1; I2C1->MDR = memAddr; I2C1->MCS = 3; // S-(addr+w)-ACK-memAddr-ACK error = I2C1_wait_till_done(); if (error) return error; I2C1->MSA = (slaveAddr << 1) + 1; // restart: -R-(addr+r)-ACK I2C1->MCS = 7; // -S-(addr+r)-NACK-P error = I2C1_wait_till_done(); if (error) return error; *data = I2C1->MDR; while(I2C1->MCS & 0x40); // wait until bus is not busy error = I2C1->MCS & 0xE; return error; } // Read memory // read: S-(saddr+w)-ACK-maddr-ACK-R-(saddr+r)-ACK-data-ACK-data-ACK-...-data-NACK-P int I2C1_burstRead(int slaveAddr, unsigned char memAddr, int byteCount, unsigned char* data) { char error; if (byteCount <= 0) return -1; // no read was performed // send slave address and starting address I2C1->MSA = slaveAddr << 1; I2C1->MDR = memAddr; I2C1->MCS = 3; // S-(saddr+w)-ACK-maddr-ACK error = I2C1_wait_till_done(); if (error) return error; // to change bus from write to read, send restart with slave addr I2C1->MSA = (slaveAddr << 1) + 1; // restart: -R-(saddr+r)-ACK if (byteCount == 1) // if last byte, don't ack I2C1->MCS = 7; // -data-NACK-P else // else ack I2C1->MCS = 0xB; // -data-ACK- error = I2C1_wait_till_done(); if (error) return error; *data++ = I2C1->MDR; // store the data received if (--byteCount == 0) { // if single byte read, done while(I2C1->MCS & 0x40); // wait until bus is not busy return 0; // no error } // read the rest of the bytes while (byteCount > 1) { I2C1->MCS = 9; // -data-ACK- error = I2C1_wait_till_done(); if (error) return error; byteCount--; *data++ = I2C1->MDR; // store data received } I2C1->MCS = 5; // -data-NACK-P error = I2C1_wait_till_done(); *data = I2C1->MDR; // store data received while(I2C1->MCS & 0x40); // wait until bus is not busy return 0; // no error } // Poll until acked. Memory chip does not ack while programming is in progress. void I2C1_ackPoll(int slaveAddr) { unsigned char error; do { // write a byte: S-(addr+w)-ACK-memAddr-ACK-data-ACK-P I2C1->MSA = slaveAddr << 1; I2C1->MDR = 0; I2C1->MCS = 3; // S-(addr+w)-ACK-memAddr-ACK error = I2C1_wait_till_done(); // wait until write is complete } while (error & (1 << 2)); // repeat until acked I2C1->MCS = 1 << 2; // generate stop } /* delay n milliseconds (50 MHz CPU clock) */ void delayMs(int n) { int i, j; for(i = 0 ; i< n; i++) for(j = 0; j < 6265; j++) {} /* do nothing for 1 ms */ } #define SYSCLOCK 50000000 #define SCICLOCK SYSCLOCK/16 #define BAUDINT(baud) SCICLOCK / baud #define BAUDFRAC(baud) (((SCICLOCK * 128 - SCICLOCK / baud * 128 * baud) + baud) / baud / 2) void UART_init(int baud) { if((CoreDebug->DHCSR & 1) == 0) // if no hardware, don't do initialization return; SYSCTL->RCGCUART |= 1; // provide clock to UART0 SYSCTL->RCGCGPIO |= 1; // enable clock to PORTA SYSCTL->RCGC2 |= 1; // UART0 initialization UART0->CTL = 0; // disable UART0 UART0->IBRD = BAUDINT(baud); UART0->FBRD = BAUDFRAC(baud); UART0->CC = 0; // use system clock UART0->LCRH = 0x60; // 8-bit, no parity, 1-stop bit, no FIFO UART0->CTL = 0x301; // enable UART0, TXE, RXE // UART0 TX0 and RX0 use PA0 and PA1. Set them up. GPIOA->DEN = 0x03; // Make PA0 and PA1 as digital GPIOA->AFSEL = 0x03; // Use PA0,PA1 alternate function GPIOA->PCTL = 0x11; // configure PA0 and PA1 for UART } int UART0Tx(unsigned char c){ while((UART0->FR & 0x20) != 0); // wait until Tx buffer not full UART0->DR = c; // before giving it another byte return c; } /* The code below is the interface to the C standard I/O library. * All the I/O are directed to the console, which is UART3. */ struct __FILE { int handle; }; FILE __stdin = {0}; FILE __stdout = {1}; int fputc(int c, FILE *f) { return (UART0Tx(c)); /* debugging hardware used */ }