Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arguably missing an "a "


### Interpolator

Expand Down
Binary file added i2c/24lc32_i2c/24lc32_i2c.fzz
Binary file not shown.
Binary file added i2c/24lc32_i2c/24lc32_i2c.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 101 additions & 0 deletions i2c/24lc32_i2c/24lc32_i2c_lib.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include <stdio.h>
#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
Comment on lines +5 to +9
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterharperuk Would it make sense for these to be in the .h file rather than the .c file? 🤷‍♂️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be a way to query the hardware for these values rather than hard code them. But maybe this is good enough for this example


// 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};
Comment on lines +33 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterharperuk Should these variables be declared outside of the for loop, or does that not really matter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it makes more sense as it is here?


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);
}

22 changes: 22 additions & 0 deletions i2c/24lc32_i2c/24lc32_i2c_lib.h
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions i2c/24lc32_i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
30 changes: 30 additions & 0 deletions i2c/24lc32_i2c/README.adoc
Original file line number Diff line number Diff line change
@@ -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
|===
70 changes: 70 additions & 0 deletions i2c/24lc32_i2c/example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <stdio.h>
#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
}
1 change: 1 addition & 0 deletions i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()