diff --git a/README.md b/README.md index f43a7339a..67a7a8270 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ App|Description [ht16k33_i2c](i2c/ht16k33_i2c) | Drive a 4 digit 14 segment LED with an HT16K33. [slave_mem_i2c](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. [slave_mem_i2c_burst](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. This version inefficiently writes each byte in a separate call to demonstrate read and write burst mode. +[24lc32_i2c](i2c/24lc32_i2c) | Read and write data to 24LC32 EEPROM via I2C. ### Interpolator diff --git a/i2c/24lc32_i2c/24lc32_i2c.fzz b/i2c/24lc32_i2c/24lc32_i2c.fzz new file mode 100644 index 000000000..c30e21fd3 Binary files /dev/null and b/i2c/24lc32_i2c/24lc32_i2c.fzz differ diff --git a/i2c/24lc32_i2c/24lc32_i2c.png b/i2c/24lc32_i2c/24lc32_i2c.png new file mode 100644 index 000000000..a0732fbaa Binary files /dev/null and b/i2c/24lc32_i2c/24lc32_i2c.png differ diff --git a/i2c/24lc32_i2c/24lc32_i2c_lib.c b/i2c/24lc32_i2c/24lc32_i2c_lib.c new file mode 100644 index 000000000..68f68c321 --- /dev/null +++ b/i2c/24lc32_i2c/24lc32_i2c_lib.c @@ -0,0 +1,101 @@ +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +// This is the max number of bytes which can be written at once during a page write +#define MAX_PAGE_WRITE 32 + +// This is the 24LC32's capacity in bytes +#define EEPROM_SIZE 4096 + +// Acknowledge polling can be used to see when a write cycle is complete +// Wait for the i2c slave to acknowledge our empty write +bool ack_poll(uint8_t i2c_addr, i2c_inst_t *i2c_instance) { + int timeout = 100; + for (int i = 0; i < timeout; i++) { + if (i2c_write_blocking(i2c_instance, i2c_addr, NULL, 1, false) == 1) { + return true; + } + sleep_ms(1); + } + printf("ack timeout\n"); + return false; +} + +// Read num bytes of memory starting at given address into result +bool read_eeprom(uint16_t address, uint8_t *result, uint16_t num, uint8_t i2c_addr, i2c_inst_t *i2c_instance) { + if ((address + num - 1) > EEPROM_SIZE) { + printf("ERROR: Tried to read memory outside EEPROM's range.\n"); + return false; + } + + for (int i = 0; i < num; i++) { + uint8_t byte1 = (uint8_t)(address >> 8); + uint8_t byte2 = (uint8_t)(address); + + uint8_t buf[2] = {byte1, byte2}; + + i2c_write_blocking(i2c_instance, i2c_addr, buf, 2, true); + i2c_read_blocking(i2c_instance, i2c_addr, (result + i), 1, false); + + address++; + } + + return true; +} + +// Write a single byte of data at a specified address in memory +bool byte_write_eeprom(uint16_t address, uint8_t data, uint8_t i2c_addr, i2c_inst_t *i2c_instance) { + if (address >= EEPROM_SIZE) { + printf("ERROR: Tried to write to memory outside EEPROM's range.\n"); + return false; + } + + uint8_t byte1 = (uint8_t)(address >> 8); + uint8_t byte2 = (uint8_t)(address); + + uint8_t buf[3] = {byte1, byte2, data}; + + i2c_write_blocking(i2c_instance, i2c_addr, buf, 3, false); + + // ack_poll to wait for write to be complete + return ack_poll(i2c_addr, i2c_instance); +} + +// Write a block of data to eeprom +bool page_write_eeprom(uint16_t address, uint8_t *data, uint8_t num, uint8_t i2c_addr, i2c_inst_t *i2c_instance) { + if (num > MAX_PAGE_WRITE) { + printf("ERROR: Tried to write more than %i bytes.\n", MAX_PAGE_WRITE); + return false; + } else if ((address + num - 1) > EEPROM_SIZE) { + printf("ERROR: Tried to write to memory outside EEPROM's range.\n"); + return false; + } + + uint8_t byte1 = (uint8_t)(address >> 8); + uint8_t byte2 = (uint8_t)(address); + uint8_t buf[2 + num]; + buf[0] = byte1; + buf[1] = byte2; + + // transfer data into buffer + for (int i = 0; i < num; i++) { + buf[i + 2] = data[i]; + } + + i2c_write_blocking(i2c_instance, i2c_addr, buf, 2 + num, false); + + // ack_poll to wait for write to be complete + return ack_poll(i2c_addr, i2c_instance); +} + +// Initialise i2c for given gpio at given frequency +void eeprom_init(int sda_pin, int scl_pin, int freq, i2c_inst_t *i2c_instance) { + i2c_init(i2c_instance, freq); + + gpio_set_function(sda_pin, GPIO_FUNC_I2C); + gpio_set_function(scl_pin, GPIO_FUNC_I2C); + gpio_pull_up(sda_pin); + gpio_pull_up(scl_pin); +} + diff --git a/i2c/24lc32_i2c/24lc32_i2c_lib.h b/i2c/24lc32_i2c/24lc32_i2c_lib.h new file mode 100644 index 000000000..854fc6efa --- /dev/null +++ b/i2c/24lc32_i2c/24lc32_i2c_lib.h @@ -0,0 +1,22 @@ +#ifndef PICO_I2C_EEPROM_LIB_H +#define PICO_I2C_EEPROM_LIB_H + +#include "hardware/i2c.h" + +// Acknowledge polling can be used to see when a write cycle is complete +// Wait for the i2c slave to acknowledge our empty write +bool ack_poll(uint8_t i2c_addr, i2c_inst_t *i2c_instance); + +// Read num bytes of memory starting at given address into result +bool read_eeprom(uint16_t address, uint8_t *result, uint16_t num, uint8_t i2c_addr, i2c_inst_t *i2c_instance); + +// Write a single byte of data at a specified address in memory +bool byte_write_eeprom(uint16_t address, uint8_t data, uint8_t i2c_addr, i2c_inst_t *i2c_instance); + +// Write a block of data to eeprom +bool page_write_eeprom(uint16_t address, uint8_t *data, uint8_t num, uint8_t i2c_addr, i2c_inst_t *i2c_instance); + +// Initialise i2c for given gpio at given frequency +void eeprom_init(int sda_pin, int scl_pin, int freq, i2c_inst_t *i2c_instance); + +#endif \ No newline at end of file diff --git a/i2c/24lc32_i2c/CMakeLists.txt b/i2c/24lc32_i2c/CMakeLists.txt new file mode 100644 index 000000000..0a770682d --- /dev/null +++ b/i2c/24lc32_i2c/CMakeLists.txt @@ -0,0 +1,28 @@ +# Add libary +add_library(24lc32_i2c_lib INTERFACE) +target_sources(24lc32_i2c_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/24lc32_i2c_lib.c + ) +target_include_directories(24lc32_i2c_lib INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ) +target_link_libraries(24lc32_i2c_lib INTERFACE + pico_stdlib + hardware_i2c + ) + +# Add executable +add_executable(24lc32_i2c + example.c + ) + +target_link_libraries(24lc32_i2c + 24lc32_i2c_lib + pico_stdlib + hardware_i2c + ) + +pico_add_extra_outputs(24lc32_i2c) + +# add url via pico_set_program_url +example_auto_set_url(24lc32_i2c) \ No newline at end of file diff --git a/i2c/24lc32_i2c/README.adoc b/i2c/24lc32_i2c/README.adoc new file mode 100644 index 000000000..b2668a6bc --- /dev/null +++ b/i2c/24lc32_i2c/README.adoc @@ -0,0 +1,30 @@ += Reading and writing 24LC32 EEPROM via I2C + +This example code demonstrates how to read and write data from 24LC32 EEPROM. It contains a library with functions for reading data, writing single bytes, and writing pages; and an example which uses these functions. + +== Wiring information + +[[24lc32_i2c_wiring]] +[pdfwidth=75%] +.Wiring Diagram for 24LC32 EEPROM via I2C. +image::24lc32_i2c.png[] + +== List of Files + +CMakeLists.txt:: CMake file to incorporate the example into the examples build tree. +24lc32_i2c_lib.c:: Library containing functions to interact with 24LC32 eeprom via I2C. +24lc32_i2c_lib.h:: Header file containing function declarations for the 24LC32 library. +example.c:: The example code. + +== Bill of Materials + +.A list of materials required for the example +[[24lc32_i2c-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico (any) | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/ +| 24LC32-based breakout board | 1 | https://www.adafruit.com/product/5146 +| M/M Jumper wires | 4 | generic part +|=== \ No newline at end of file diff --git a/i2c/24lc32_i2c/example.c b/i2c/24lc32_i2c/example.c new file mode 100644 index 000000000..2a6676e41 --- /dev/null +++ b/i2c/24lc32_i2c/example.c @@ -0,0 +1,70 @@ +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "pico/binary_info.h" +#include "24lc32_i2c_lib.h" + +// Default I2C address of 24LC32 +#define I2C_ADDRESS 0x50 + +int main() { + stdio_init_all(); + + #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) + #warning 24lc32_i2c example requires a board with i2c pins + puts("Default I2C pins were not defined"); + return 0; + #else + // useful information for picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + bi_decl(bi_program_description("24LC32 I2C example for the Raspberry Pi Pico")); + + eeprom_init(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, 400*1000, i2c_default); + + printf("24LC32 I2C EEPROM example\nStarting test...\n\n"); + + // Write the byte at location 0x00 to 0 + byte_write_eeprom(0x00, 0, I2C_ADDRESS, i2c_default); + + // Make an array of integers from 1 to 31 and write the array to eeprom starting at 0x01 + uint8_t data_list[31]; + for (int i = 0; i < 31; i++) { + data_list[i] = i + 1; + } + page_write_eeprom(0x01, data_list, 31, I2C_ADDRESS, i2c_default); + + // Read eeprom, and ensure we observe ascending integers from 0 to 31 + uint8_t result[32]; + read_eeprom(0x0, result, 32, I2C_ADDRESS, i2c_default); + + for (int i = 0; i < 32; i++) { + printf("read: %i, expected: %i\n", result[i], i); + if (result[i] != i) { + printf("Unexpected read!\n"); + return 1; + } + } + + // Now re-write and re-read eeprom + printf("\nReversing order...\n\n"); + for (int i = 0; i < 31; i++) { + data_list[i] = 31 - i; + } + page_write_eeprom(0x00, data_list, 31, I2C_ADDRESS, i2c_default); + byte_write_eeprom(0x1F, 0, I2C_ADDRESS, i2c_default); + + // Read eeprom, and ensure we observe descending integers from 31 to 0 + read_eeprom(0x0, result, 32, I2C_ADDRESS, i2c_default); + + for (int i = 0; i < 32; i++) { + printf("read: %i, expected: %i\n", result[i], 31 - i); + if (result[i] != 31 - i) { + printf("Unexpected read!\n"); + return 1; + } + } + + printf("Test passed!\n"); + return 0; + #endif +} \ No newline at end of file diff --git a/i2c/CMakeLists.txt b/i2c/CMakeLists.txt index 6b9c1ebef..88119316a 100644 --- a/i2c/CMakeLists.txt +++ b/i2c/CMakeLists.txt @@ -12,6 +12,7 @@ if (TARGET hardware_i2c) add_subdirectory_exclude_platforms(pcf8523_i2c) add_subdirectory_exclude_platforms(ht16k33_i2c) add_subdirectory_exclude_platforms(slave_mem_i2c) + add_subdirectory_exclude_platforms(24lc32_i2c) else() message("Skipping I2C examples as hardware_i2c is unavailable on this platform") endif()