diff --git a/doc/I2C_Lab_Pt3.pdf b/doc/I2C_Lab_Pt3.pdf new file mode 100644 index 0000000..b87c330 Binary files /dev/null and b/doc/I2C_Lab_Pt3.pdf differ diff --git a/projects/lpc40xx_freertos/assignment_include.h b/projects/lpc40xx_freertos/assignment_include.h index 2d537bb..7a129b5 100644 --- a/projects/lpc40xx_freertos/assignment_include.h +++ b/projects/lpc40xx_freertos/assignment_include.h @@ -15,4 +15,7 @@ * Take care to enable only one part macro enabled while compiling. */ -#endif \ No newline at end of file +#define I2C_LAB_ASSGNMT +//#define I2C_LAB_ASSGNMT_PART_A +#define I2C_LAB_ASSGNMT_PART_B +#endif \ No newline at end of file diff --git a/projects/lpc40xx_freertos/l3_drivers/i2c.h b/projects/lpc40xx_freertos/l3_drivers/i2c.h index 23f53be..44190d1 100644 --- a/projects/lpc40xx_freertos/l3_drivers/i2c.h +++ b/projects/lpc40xx_freertos/l3_drivers/i2c.h @@ -67,3 +67,5 @@ bool i2c__write_single(i2c_e i2c_number, uint8_t slave_address, uint8_t slave_me */ bool i2c__write_slave_data(i2c_e i2c_number, uint8_t slave_address, uint8_t starting_slave_memory_address, const uint8_t *bytes_to_write, uint32_t number_of_bytes); + +void i2c2__slave_init(uint8_t slave_address_to_respond_to); diff --git a/projects/lpc40xx_freertos/l3_drivers/i2c_slave_function.h b/projects/lpc40xx_freertos/l3_drivers/i2c_slave_function.h new file mode 100644 index 0000000..b3df06b --- /dev/null +++ b/projects/lpc40xx_freertos/l3_drivers/i2c_slave_function.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +/** + * This function is responsible for reading slave memory + * @param memory_index The index of Slave memory need to be read + * @param *memory Reference of variable to be updated with salve memory data + */ +bool i2c_slave_callback__read_memory(uint8_t memory_index, uint8_t *memory); + +/** + * This function is responsible for writing slave memory + * @param memory_index The index of Slave memory need to be read + * @param *memory Reference of variable to be updated with salve memory data + */ +bool i2c_slave_callback__write_memory(uint8_t memory_index, uint8_t memory_value); \ No newline at end of file diff --git a/projects/lpc40xx_freertos/l3_drivers/sources/i2c.c b/projects/lpc40xx_freertos/l3_drivers/sources/i2c.c index 701c03f..e7fcedb 100644 --- a/projects/lpc40xx_freertos/l3_drivers/sources/i2c.c +++ b/projects/lpc40xx_freertos/l3_drivers/sources/i2c.c @@ -5,11 +5,12 @@ #include "task.h" #include "common_macros.h" +#include "i2c_slave_function.h" #include "lpc40xx.h" #include "lpc_peripherals.h" /// Set to non-zero to enable debugging, and then you can use I2C__DEBUG_PRINTF() -#define I2C__ENABLE_DEBUGGING 0 +#define I2C__ENABLE_DEBUGGING 1 #if I2C__ENABLE_DEBUGGING #include @@ -44,6 +45,11 @@ typedef struct { uint8_t *input_byte_pointer; ///< Used for reading I2C slave device const uint8_t *output_byte_pointer; ///< Used for writing data to the I2C slave device size_t number_of_bytes_to_transfer; + + // Slave parameters + bool slave_data_address_received; + uint8_t slave_data_address; + uint8_t slave_data_count; } i2c_s; /// Instances of structs for each I2C peripheral @@ -153,6 +159,18 @@ void i2c__initialize(i2c_e i2c_number, uint32_t desired_i2c_bus_speed_in_hz, uin lpc_peripheral__enable_interrupt(peripheral_id, isrs[i2c_number], i2c_structs[i2c_number].rtos_isr_trace_name); } +#ifdef I2C_LAB_ASSGNMT + +void i2c2__slave_init(uint8_t slave_address_to_respond_to) { + const uint8_t device_address_bitmask = (0b11111110); + const uint8_t i2c_enable_bit_mask = (1 << 6); + const uint8_t ack_own_slave_addr_bit_mask = (1 << 2); + LPC_I2C0->ADR0 |= ((slave_address_to_respond_to << 1) & device_address_bitmask); + LPC_I2C0->CONSET |= (i2c_enable_bit_mask | ack_own_slave_addr_bit_mask); +} + +#endif + bool i2c__detect(i2c_e i2c_number, uint8_t slave_address) { // The I2C State machine will not continue after 1st state when length is set to 0 const size_t zero_bytes = 0; @@ -233,6 +251,7 @@ static bool i2c__transfer_unprotected(i2c_s *i2c, uint8_t slave_address, uint8_t // We cannot block on the semaphore if the RTOS is not running, so eat the CPU until transaction is done while (!xSemaphoreTake(i2c->transfer_complete_signal, 0)) { } + printf("Error Code is %d\n", i2c->error_code); status = (0 == i2c->error_code); } @@ -274,6 +293,20 @@ static bool i2c__handle_state_machine(i2c_s *i2c) { I2C__STATE_MR_SLAVE_READ_NACK = 0x48, I2C__STATE_MR_SLAVE_ACK_SENT = 0x50, I2C__STATE_MR_SLAVE_NACK_SENT = 0x58, + +#ifdef I2C_LAB_ASSGNMT_PART_B + // Slave Receiver Mode (SR): + I2C__STATE_SR_SLAVE_ADDRESS_RECEIVED = 0x60, + I2C__STATE_SR_MASTER_DATA_RECEIVED = 0x80, + I2C__STATE_SR_MASTER_LAST_DATA_RECEIVED = 0x88, + I2C__STATE_SR_MASTER_STOP_RECEIVED = 0xA0, + + // Slave Trasmitter Mode (ST): + I2C__STATE_ST_SLAVE_ADDRESS_RECEIVED = 0xA8, + I2C__STATE_ST_MASTER_ACK_RECEIVED = 0xB8, + I2C__STATE_ST_MASTER_NACK_RECEIVED = 0xC0, + I2C__STATE_ST_LAST_DATA_BYTE_TRANS_ACK_RECEIVED = 0xC8, +#endif }; bool stop_sent = false; @@ -295,6 +328,8 @@ static bool i2c__handle_state_machine(i2c_s *i2c) { const unsigned i2c_state = lpc_i2c->STAT; I2C__DEBUG_PRINTF(" HW State: 0x%02X", i2c_state); + uint8_t data_received_from_master; + uint8_t read_slave_data; switch (i2c_state) { // Start condition sent, so send the device address case I2C__STATE_START: @@ -382,6 +417,67 @@ static bool i2c__handle_state_machine(i2c_s *i2c) { i2c->error_code = lpc_i2c->STAT; break; +#ifdef I2C_LAB_ASSGNMT_PART_B + + case I2C__STATE_SR_SLAVE_ADDRESS_RECEIVED: + i2c__set_ack_flag(lpc_i2c); + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + i2c->slave_data_address_received = false; + break; + + case I2C__STATE_SR_MASTER_DATA_RECEIVED: + data_received_from_master = (lpc_i2c->DAT & 0xFF); + if (!i2c->slave_data_address_received) { + i2c->slave_data_address = data_received_from_master; + i2c->slave_data_count = 0; + i2c__set_ack_flag(lpc_i2c); + i2c->slave_data_address_received = true; + } else { + if (i2c_slave_callback__write_memory(i2c->slave_data_address + i2c->slave_data_count, + data_received_from_master)) { + i2c__set_ack_flag(lpc_i2c); + i2c->slave_data_count++; + } else { + i2c__set_nack_flag(lpc_i2c); + } + } + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + break; + + case I2C__STATE_SR_MASTER_STOP_RECEIVED: + i2c__set_ack_flag(lpc_i2c); + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + break; + + case I2C__STATE_ST_SLAVE_ADDRESS_RECEIVED: + fprintf(stderr, "read data from %d\n", i2c->slave_data_address); + if (i2c_slave_callback__read_memory(i2c->slave_data_address, &read_slave_data)) { + lpc_i2c->DAT = read_slave_data; + i2c__set_ack_flag(lpc_i2c); + i2c->slave_data_count++; + } else { + i2c__set_nack_flag(lpc_i2c); + } + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + break; + + case I2C__STATE_ST_MASTER_ACK_RECEIVED: + if (i2c_slave_callback__read_memory(i2c->slave_data_address + i2c->slave_data_count, &read_slave_data)) { + lpc_i2c->DAT = read_slave_data; + i2c__set_ack_flag(lpc_i2c); + i2c->slave_data_count++; + } else { + i2c__set_nack_flag(lpc_i2c); + } + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + break; + + case I2C__STATE_ST_MASTER_NACK_RECEIVED: + // i2c__set_ack_flag(lpc_i2c); + i2c__clear_si_flag_for_hw_to_take_next_action(lpc_i2c); + break; + +#endif case I2C__STATE_MT_SLAVE_ADDR_NACK: // no break case I2C__STATE_MT_SLAVE_DATA_NACK: // no break case I2C__STATE_MR_SLAVE_READ_NACK: // no break diff --git a/projects/lpc40xx_freertos/l3_drivers/sources/i2c_slave_function.c b/projects/lpc40xx_freertos/l3_drivers/sources/i2c_slave_function.c new file mode 100644 index 0000000..63e3f05 --- /dev/null +++ b/projects/lpc40xx_freertos/l3_drivers/sources/i2c_slave_function.c @@ -0,0 +1,34 @@ +#include "i2c_slave_function.h" +#include "i2c.h" + +#define SLAVE_MEMORY_SIZE 10 + +/** + * Register number = memory Index + * Reg 0: On/Off + * | Led 3 | Led 2 | Led 1 | Led 0 | | | | | + * | Reserved | + * + * Reg 1: Blinking + * | Led3 | Led2 | Led 1| Led 0| | | | on/off(blinking)| + * | Blinking duration| + */ + +static volatile uint8_t slave_memory[SLAVE_MEMORY_SIZE]; + +bool i2c_slave_callback__read_memory(uint8_t memory_index, uint8_t *memory) { + if (memory_index < SLAVE_MEMORY_SIZE) { + *memory = slave_memory[memory_index]; + } else { + return false; + } + return true; +} + +bool i2c_slave_callback__write_memory(uint8_t memory_index, uint8_t memory_value) { + if (memory_index < SLAVE_MEMORY_SIZE) + slave_memory[memory_index] = memory_value; + else + return false; + return true; +} \ No newline at end of file diff --git a/projects/lpc40xx_freertos/l5_application/main.c b/projects/lpc40xx_freertos/l5_application/main.c index db7ae27..aeaf937 100644 --- a/projects/lpc40xx_freertos/l5_application/main.c +++ b/projects/lpc40xx_freertos/l5_application/main.c @@ -5,19 +5,48 @@ #include "board_io.h" #include "common_macros.h" +#include "i2c.h" +#include "i2c_slave_function.h" #include "periodic_scheduler.h" #include "sj2_cli.h" +//#define I2C_MASTER +#define I2C_SLAVE + // 'static' to make these functions 'private' to this file static void create_blinky_tasks(void); static void create_uart_task(void); static void blink_task(void *params); static void uart_task(void *params); +static void i2c_task(void *p); +static void pin_select_for_i2c(); +static void i2c_mt_task(void *p); +static void i2c_slave_task(void *p); int main(void) { - create_blinky_tasks(); - create_uart_task(); +#ifdef I2C_LAB_ASSGNMT + const uint32_t desired_i2c_bus_speed_in_hz = 100 * (1000); + const uint8_t i2c_slave_device_address = 0x07; + i2c__initialize(I2C__0, desired_i2c_bus_speed_in_hz, clock__get_peripheral_clock_hz()); + pin_select_for_i2c(); +#ifdef I2C_SLAVE + i2c2__slave_init(i2c_slave_device_address); +#ifdef I2C_LAB_ASSGNMT_PART_B + xTaskCreate(i2c_slave_task, "i2c_slave", 1024 / sizeof(void *), NULL, PRIORITY_LOW, NULL); +#endif +#endif +#ifdef I2C_MASTER +#ifdef I2C_LAB_ASSGNMT_PART_A + xTaskCreate(i2c_task, "i2c", 1024 / sizeof(void *), NULL, PRIORITY_LOW, NULL); +#endif +#ifdef I2C_LAB_ASSGNMT_PART_B + xTaskCreate(i2c_mt_task, "I2C_MT", 1024 / sizeof(void *), NULL, PRIORITY_LOW, NULL); +#endif +#endif + // create_blinky_tasks(); + create_uart_task(); +#endif // If you have the ESP32 wifi module soldered on the board, you can try uncommenting this code // See esp32/README.md for more details // uart3_init(); // Also include: uart3_init.h @@ -29,6 +58,130 @@ int main(void) { return 0; } +#ifdef I2C_LAB_ASSGNMT + +#ifdef I2C_MASTER + +#ifdef I2C_LAB_ASSGNMT_PART_A +void i2c_task(void *p) { + while (1) { + if (i2c__detect(I2C__0, 0x0E)) { + printf("Successfully detected slave\n"); + } else { + printf("Failed to detected slave\n"); + } + vTaskDelay(1000); + } +} +#endif + +#ifdef I2C_LAB_ASSGNMT_PART_B + +void i2c_mt_task(void *p) { + const uint8_t byte_to_send = 0xA4; + const uint8_t clear_byte_data = 0x00; + uint8_t slave_data_read; + + while (1) { + // Clearing the register 0, before writing register 1, as per slave requirement + if (i2c__write_slave_data(I2C__0, 0x0E, 0, &clear_byte_data, 1)) { + printf("Sent data %d successfully to Slave register 0\n", clear_byte_data); + } else { + printf("Failed to send data %d to slave register 0\n", clear_byte_data); + } + if (i2c__write_slave_data(I2C__0, 0x0E, 1, &byte_to_send, 1)) { + printf("Sent data %d successfully to Slave register 1\n", byte_to_send); + } else { + printf("Failed to send data %d to slave register 1\n", byte_to_send); + } + // if (i2c__read_slave_data(I2C__0, 0x0E, 1, &slave_data_read, 1)) { + // if (slave_data_read == byte_to_send) { + // printf("Successfully read data %d \n", slave_data_read); + // } else { + // printf("Data read is %d, which is not same as written\n", slave_data_read); + // } + // } else { + // printf("Failed to read data \n"); + // } + vTaskDelay(1000); + } +} +#endif + +#endif + +#ifdef I2C_SLAVE +#ifdef I2C_LAB_ASSGNMT_PART_B + +static void i2c_slave_function_handler(uint8_t register_index, uint8_t register_data) { + const uint8_t led0_bit_mask = (1 << 4); + const uint8_t led1_bit_mask = (1 << 5); + const uint8_t led2_bit_mask = (1 << 6); + const uint8_t led3_bit_mask = (1 << 7); + const uint8_t blink_duration_in_sec_bit_mask = (0b111); + gpio_s led0 = board_io__get_led0(); + gpio_s led1 = board_io__get_led1(); + gpio_s led2 = board_io__get_led2(); + gpio_s led3 = board_io__get_led3(); + + switch (register_index) { + case 1: + printf("Toggling LEDs"); + if (register_data & led0_bit_mask) { + printf(" led0"); + gpio__toggle(led0); + } + if (register_data & led1_bit_mask) { + printf(" led1"); + gpio__toggle(led1); + } + if (register_data & led2_bit_mask) { + printf(" led2"); + gpio__toggle(led2); + } + if (register_data & led3_bit_mask) { + printf(" led3"); + gpio__toggle(led3); + } + uint32_t blink_duration_in_ms = ((register_data >> 1) & blink_duration_in_sec_bit_mask) * 1000; + printf(" with gap of %ld ms\n", blink_duration_in_ms); + vTaskDelay(blink_duration_in_ms); + break; + + case 0: + (register_data & led0_bit_mask) ? gpio__reset(led0) : gpio__set(led0); + (register_data & led1_bit_mask) ? gpio__reset(led1) : gpio__set(led1); + (register_data & led2_bit_mask) ? gpio__reset(led2) : gpio__set(led2); + (register_data & led3_bit_mask) ? gpio__reset(led3) : gpio__set(led3); + break; + } +} + +void i2c_slave_task(void *p) { + + while (1) { + uint8_t slave_memory_value; + // LED on/off register index 0 + // i2c_slave_callback__read_memory(0, &slave_memory_value); + // i2c_slave_function_handler(0, slave_memory_value); + // LED blinking register index 1 + i2c_slave_callback__read_memory(1, &slave_memory_value); + i2c_slave_function_handler(1, slave_memory_value); + + vTaskDelay(100); + } +} + +#endif +#endif + +static void pin_select_for_i2c() { + const uint32_t i2c_function_bit = 0b100; + const uint32_t od_bit_mask = (1 << 10); + LPC_IOCON->P1_30 |= (od_bit_mask | i2c_function_bit); + LPC_IOCON->P1_31 |= (od_bit_mask | i2c_function_bit); +} + static void create_blinky_tasks(void) { /** * Use '#if (1)' if you wish to observe how two tasks can blink LEDs @@ -103,3 +256,4 @@ static void uart_task(void *params) { printf(" %lu ticks\n\n", (xTaskGetTickCount() - ticks)); } } +#endif \ No newline at end of file