commit 983b422ddd189b79c0dcb3f61952ad8ad7b01e00 Author: Ulysse Cura Date: Fri Jan 16 19:21:09 2026 +0100 First Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25ce69c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +master/build +slave/build diff --git a/master/.vscode/c_cpp_properties.json b/master/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..643d42c --- /dev/null +++ b/master/.vscode/c_cpp_properties.json @@ -0,0 +1,33 @@ +{ + "env": { + "myDefaultIncludePath": [ + "${workspaceFolder}/src/include/", + "${env:PICO_SDK_PATH}/src/**/include/", + "${env:PICO_SDK_PATH}/lib/**/include/", + "${workspaceFolder}/build/generated/pico_base/" + ], + "myCompilerPath": "/usr/bin/arm-none-eabi-gcc" + }, + + "configurations": [ + { + "name": "Linux", + "intelliSenseMode": "linux-gcc-arm", + "includePath": [ + "${myDefaultIncludePath}" + ], + "compilerPath": "/usr/bin/arm-none-eabi-gcc", + "cStandard": "c11", + "cppStandard": "c++17", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/master/.vscode/settings.json b/master/.vscode/settings.json new file mode 100644 index 0000000..cb0aebb --- /dev/null +++ b/master/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "files.associations": { + "*.md": "markdown", + "binary_info.h": "c", + "i2c.h": "c", + "stdlib.h": "c", + "stdint.h": "c", + "gyro.h": "c", + "motors.h": "c", + "motion_control.h": "c", + "i2c_master.h": "c", + "udp_client.h": "c", + "udp_payload.h": "c", + "udp.h": "c", + "opt.h": "c", + "cyw43_arch.h": "c", + "wifi_operator.h": "c" + } +} \ No newline at end of file diff --git a/master/.vscode/tasks.json b/master/.vscode/tasks.json new file mode 100644 index 0000000..1255bf5 --- /dev/null +++ b/master/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + "tasks": [ + { + "type": "shell", + "command": "cd build; cmake -DPICO_BOARD=pico_w ..; make", + "label": "CMake in build/", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": false + } + }, + { + "type": "shell", + "command": "cd build; cmake -DPICO_BOARD=pico_w ..; make Flash", + "label": "CMake & Make & Flash", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/master/CMakeLists.txt b/master/CMakeLists.txt new file mode 100644 index 0000000..2c2b173 --- /dev/null +++ b/master/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(main_controller C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR}) + +# Définir explicitement la carte comme Pico W +set(PICO_BOARD pico_w) +if(NOT DEFINED PICO_BOARD) + add_definitions(-DPICO_BOARD=${PICO_BOARD}) +endif() + +pico_sdk_init() + +add_executable(main_controller + src/main.c + src/i2c_master.c +) + +target_include_directories(main_controller PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}/src/include +) + +target_link_libraries(main_controller + hardware_i2c + hardware_pwm + hardware_uart + pico_stdlib +) + +pico_enable_stdio_usb(main_controller 1) +pico_enable_stdio_uart(main_controller 1) + +pico_add_extra_outputs(main_controller) + +add_custom_target(Flash + DEPENDS main_controller + COMMAND sudo picotool load -f ${PROJECT_BINARY_DIR}/main_controller.uf2 +) diff --git a/master/Readme.md b/master/Readme.md new file mode 100644 index 0000000..76447b8 --- /dev/null +++ b/master/Readme.md @@ -0,0 +1,95 @@ +Motion controler code for the RPI Pico (RP2040) +=============================================== + +This project is the main controller firmware for the RPI Pico (RP2040), designed for the Eurobot 2025 Cup. + + +I2C description +----------------------------------------------- + +The robot’s I2C communication works as follows: + * Send the device address + R/W bit (to select read or write mode). + * Send the target register address (to read from or write to). + * Read or write the register data. Multiple registers can be read/written sequentially, with the address auto-incrementing after each operation. + +This code is designed to be the master in the i2c communication. + +|Register |R/W|Description |Encoding | +|---------|:-:|-------------------------------|:-----------------:| +| 0x00 | W | Speed motor 1 |**-128** - **127** | +| 0x01 | W | Speed motor 2 |**-128** - **127** | +| 0x02 | W | Speed motor 3 |**-128** - **127** | +| 0x03 | W | Speed motor 4 |**-128** - **127** | +| 0x04 | W | Servo 1 position selection | **0** - **1** | +| 0x05 | W | Servo 2 position selection | **0** - **1** | +| 0x06 | W | Servo 3 position selection | **0** - **1** | +| 0x07 | W | Servo 4 position selection | **0** - **1** | + + +Motors communication description +----------------------------------------------- + +Motors are «connected» to their respective I2C buffer address. + +To control a motor you need to write data to its adress of the form : + +>```C +>int8_t speed; +>``` + +Value goes from **-128** to **127**. + + +Servo motors communication description +----------------------------------------------- + +Servo motors are «connected» to their respective I2C buffer address. + +To control a servo motor you need to write data to its adress of the form : + +>```C +>uint8_t close; +>``` + +Value is 0 or 1 for the open or the close pos. + + +Internet communication description +----------------------------------------------- + +The robot main_controller is a client connected to the wireless controller which is an udp server host. A buffer is used to store data received from host. + +Speed on X and Y axis are not depending of the robot orientation. + +Servo motors keep the same byte address in i2c buffer and udp payload. + +|Byte |Description |Encoding | +|---------|-------------------------------------------------|:-----------------:| +| 0x00-01 | Robot angle (0x00 is the last significant byte) |**-180** - **180** | +| 0x02 | Speed x axis |**-128** - **127** | +| 0x03 | Speed y axis |**-128** - **127** | +| 0x04 | Servo 1 position selection | **0** - **1** | +| 0x05 | Servo 2 position selection | **0** - **1** | +| 0x06 | Servo 3 position selection | **0** - **1** | +| 0x07 | Servo 4 position selection | **0** - **1** | + + +Pinout description +----------------------------------------------- + +|Pin |Description |GPIO Type | +|----|----------------------------------|-----------| +| 4 | I2C Bus SDA | I2C | +| 5 | I2C Bus SCL | I2C | + + +Motors placement +----------------------------------------------- + + ,-~***~-, + /1 2\ + | | + | | + | | + \3 4/ + `-.....-' diff --git a/master/pico_sdk_import.cmake b/master/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/master/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/master/src/i2c_master.c b/master/src/i2c_master.c new file mode 100644 index 0000000..0f79ee4 --- /dev/null +++ b/master/src/i2c_master.c @@ -0,0 +1,36 @@ +#include "include/i2c_master.h" + +#include +#include + +void i2c_master_init(void) +{ + gpio_set_function(I2C_MASTER_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_MASTER_SCL_PIN, GPIO_FUNC_I2C); + + gpio_pull_up(I2C_MASTER_SDA_PIN); + gpio_pull_up(I2C_MASTER_SCL_PIN); + + i2c_init(I2C_MASTER_INSTANCE, I2C_MASTER_BAUD_RATE); +} + +void i2c_master_deinit(void) +{ + i2c_deinit(I2C_MASTER_INSTANCE); +} + +inline int i2c_master_write(uint8_t address, const uint8_t *src, size_t len) +{ + return i2c_write_blocking(I2C_MASTER_INSTANCE, address, src, len, false); +} + +inline void i2c_master_read(uint8_t address, uint8_t *dst, size_t len) +{ + i2c_read_blocking(I2C_MASTER_INSTANCE, address, dst, len, false); +} + +inline void i2c_master_read_reg(uint8_t address, uint8_t reg, uint8_t *dst, size_t len) +{ + i2c_master_write(address, ®, 1); + i2c_master_read(address, dst, len); +} \ No newline at end of file diff --git a/master/src/include/i2c_master.h b/master/src/include/i2c_master.h new file mode 100644 index 0000000..899c0bd --- /dev/null +++ b/master/src/include/i2c_master.h @@ -0,0 +1,22 @@ +#ifndef I2C_MASTER_H +#define I2C_MASTER_H + +#include + +#define I2C_MASTER_SDA_PIN 16 +#define I2C_MASTER_SCL_PIN 17 +#define I2C_MASTER_INSTANCE i2c0 +#define I2C_MASTER_BAUD_RATE 100 * 1000 + +// Init master i2c +void i2c_master_init(void); +// Deinit master i2c +void i2c_master_deinit(void); +// Send [src] of [len] to [address] and close communication +int i2c_master_write(uint8_t address, const uint8_t *src, size_t len); +// Receive [dst] of [len] from [address] and close communication +void i2c_master_read(uint8_t address, uint8_t *dst, size_t len); +// Send [reg] and receive data in [dst] and close communition +void i2c_master_read_reg(uint8_t address, uint8_t reg, uint8_t *dst, size_t len); + +#endif // I2C_MASTER_H \ No newline at end of file diff --git a/master/src/main.c b/master/src/main.c new file mode 100644 index 0000000..a143b31 --- /dev/null +++ b/master/src/main.c @@ -0,0 +1,57 @@ +/* *\ + Copyrights 2025 + Riombotique +\* */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Code du RPI Pico principale gérant les differentes entrées-sorties. * +* Ce Pico est un maitre pilotant le gyroscope, l'internet et le motion controller.* +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "include/i2c_master.h" +#include +#include + +int main(void) +{ + stdio_init_all(); + + sleep_ms(5000); + + puts("STDIO INIT DONE"); + + printf("Initialisation des broches\n"); + for(int i=0; i++; i<=28){ + if(gpio_get_function(i) == GPIO_FUNC_I2C){ + printf("Borche I2C : %d\n", i); + gpio_set_function(i, GPIO_FUNC_NULL); + } + } + + printf("%d et %d en I2C\n", I2C_MASTER_SDA_PIN, I2C_MASTER_SCL_PIN); + gpio_set_function(I2C_MASTER_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_MASTER_SCL_PIN, GPIO_FUNC_I2C); + + + i2c_master_init(); + + puts("I2C MASTER INIT DONE"); + + bool led_state = false; + + while(true) + { + uint8_t data[] = {0x00, led_state}; + int ret = i2c_master_write(0x09, data, 2); + + if(ret = PICO_ERROR_GENERIC) + puts("I2C ERROR GENERIC"); + else + printf("Written %d bytes\n", ret); + + led_state = !led_state; + sleep_ms(50); + } + + return 0; +} diff --git a/slave/.vscode/c_cpp_properties.json b/slave/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..8cc4ce6 --- /dev/null +++ b/slave/.vscode/c_cpp_properties.json @@ -0,0 +1,31 @@ +{ + "env": { + "myDefaultIncludePath": [ + "${env:PICO_SDK_PATH}/src/**/include/", + "${workspaceFolder}/build/generated/pico_base/" + ], + "myCompilerPath": "/usr/bin/arm-none-eabi-gcc" + }, + "configurations": [ + { + "name": "Linux", + "intelliSenseMode": "linux-gcc-arm", + "includePath": [ + "${myDefaultIncludePath}", + "${workspaceFolder}/build/" + ], + "compilerPath": "/usr/bin/arm-none-eabi-gcc", + "cStandard": "c11", + "cppStandard": "c++17", + "browse": { + "path": [ + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/slave/.vscode/settings.json b/slave/.vscode/settings.json new file mode 100644 index 0000000..9fcb311 --- /dev/null +++ b/slave/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "files.associations": { + "*.md": "markdown", + "binary_info.h": "c", + "i2c.h": "c", + "time.h": "c", + "stdlib.h": "c", + "robot.h": "c", + "stdio.h": "c", + "i2c_buffer.h": "c", + "motors.h": "c" + } +} \ No newline at end of file diff --git a/slave/.vscode/tasks.json b/slave/.vscode/tasks.json new file mode 100644 index 0000000..c98bf9b --- /dev/null +++ b/slave/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + "tasks": [ + { + "type": "shell", + "command": "cd build; cmake ../; make", + "label": "CMake in build/", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": false + } + }, + { + "type": "shell", + "command": "cd build; cmake ../; make Flash", + "label": "CMake & Make & Flash", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/slave/CMakeLists.txt b/slave/CMakeLists.txt new file mode 100644 index 0000000..7c9c0b0 --- /dev/null +++ b/slave/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(motion_controller C CXX ASM) +set(CMAKE_C_STNDARD 11) +set(CMAKE_CXX_STANDARD 17) + +set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR}) + +pico_sdk_init() + +add_executable(motion_controller + src/main.c + src/i2c_slave.c + src/i2c_buffer.c +) + +target_link_libraries(motion_controller + hardware_i2c + hardware_pwm + hardware_uart + pico_stdlib +) + +pico_enable_stdio_usb(motion_controller 1) +pico_enable_stdio_uart(motion_controller 1) + +pico_add_extra_outputs(motion_controller) + +add_custom_target(Flash + DEPENDS motion_controller + COMMAND sudo picotool load -f ${PROJECT_BINARY_DIR}/motion_controller.uf2 +) diff --git a/slave/Readme.md b/slave/Readme.md new file mode 100644 index 0000000..bb0c0ef --- /dev/null +++ b/slave/Readme.md @@ -0,0 +1,79 @@ +Motion controler code for the RPI Pico (RP2040) +=============================================== + +This project is a motion controller firmware for the RPI Pico (RP2040), designed for plug-and-play use with modular code and a clear architecture. + + +I2C description +----------------------------------------------- + +The robot’s I2C communication works as follows: + * Send the device address + R/W bit (to select read or write mode). + * Send the target register address (to read from or write to). + * Read or write the register data. Multiple registers can be read/written sequentially, with the address auto-incrementing after each operation. + +This firmware is coded to be a slave when adressed. Its address is **0x09** which you can change if there is any conflict whit other hardware. + +|Adress |R/W|Description |Encoding | +|-------|:-:|-------------------------------|:-----------------:| +| 0x00 | W | Speed motor 1 |**-128** - **127** | +| 0x01 | W | Speed motor 2 |**-128** - **127** | +| 0x02 | W | Speed motor 3 |**-128** - **127** | +| 0x03 | W | Speed motor 4 |**-128** - **127** | +| 0x04 | W | Servo 1 position selection | **0** - **1** | +| 0x05 | W | Servo 2 position selection | **0** - **1** | +| 0x06 | W | Servo 3 position selection | **0** - **1** | +| 0x07 | W | Servo 4 position selection | **0** - **1** | + + +Motors communication description +----------------------------------------------- + +Motors are «connected» to their respective I2C buffer address. + +To control a motor you need to write data to its adress of the form : + +>```C +>int8_t speed; +>``` + +Value goes from **-128** to **127**. + + +Servo motors communication description +----------------------------------------------- + +Servo motors are «connected» to their respective I2C buffer address. + +To control a servo motor you need to write data to its adress of the form : + +>```C +>uint8_t close; +>``` + +Value is 0 or 1 for the open pos or the close pos. + + +Pinout description +----------------------------------------------- + +|Pin |Description |GPIO Type | +|----|----------------------------------|-----------| +| 0 | Motor1 Speed | PWM | +| 1 | Motor2 Speed | PWM | +| 2 | Motor3 Speed | PWM | +| 3 | Motor4 Speed | PWM | +| 4 | Motor1 Dir1 | OUTPUT | +| 5 | Motor1 Dir2 | OUTPUT | +| 6 | Motor2 Dir1 | OUTPUT | +| 7 | Motor2 Dir2 | OUTPUT | +| 8 | Motor3 Dir1 | OUTPUT | +| 9 | Motor3 Dir2 | OUTPUT | +| 10 | Motor4 Dir1 | OUTPUT | +| 11 | Motor5 Dir2 | OUTPUT | +| 12 | Servo1 Angle | PWM | +| 13 | Servo2 Angle | PWM | +| 14 | Servo3 Angle | PWM | +| 15 | Servo4 Angle | PWM | +| 21 | I2C Bus SDA | I2C | +| 20 | I2C Bus SCL | I2C | diff --git a/slave/pico_sdk_import.cmake b/slave/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/slave/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/slave/src/i2c_buffer.c b/slave/src/i2c_buffer.c new file mode 100644 index 0000000..913c395 --- /dev/null +++ b/slave/src/i2c_buffer.c @@ -0,0 +1,41 @@ +#include "include/i2c_buffer.h" +#include + + +extern i2c_buffer_t i2c_buffer; + +void __not_in_flash_func(i2c_slave_buffer_handler)(i2c_slave_event_t event) +{ + switch(event) + { + case I2C_SLAVE_RECEIVE: // master has written some data + gpio_put(PICO_DEFAULT_LED_PIN, 1); + + if(!i2c_buffer.buffer_reg_written) + { + // writes always start with the memory address + i2c_buffer.buffer_reg = i2c_slave_read_byte(); + i2c_buffer.buffer_reg_written = true; + } + else + { + // save into memory + i2c_buffer.buffer[i2c_buffer.buffer_reg] = i2c_slave_read_byte(); + i2c_buffer.buffer_reg++; + } + break; + + case I2C_SLAVE_REQUEST: // master is requesting data + // load from memory + i2c_slave_write_byte(i2c_buffer.buffer[i2c_buffer.buffer_reg]); + i2c_buffer.buffer_reg++; + break; + + case I2C_SLAVE_FINISH: // master has signalled Stop / Restart + i2c_buffer.buffer_reg_written = false; + break; + + default: + break; + } +} diff --git a/slave/src/i2c_slave.c b/slave/src/i2c_slave.c new file mode 100644 index 0000000..a68bf8b --- /dev/null +++ b/slave/src/i2c_slave.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Valentin Milea + * + * SPDX-License-Identifier: MIT + */ + +#include "include/i2c_slave.h" + +#include +#include +#include "include/i2c_buffer.h" +#include + +static bool transfer_in_progress; + +static inline void finish_transfer(void) +{ + if(transfer_in_progress) + { + i2c_slave_buffer_handler(I2C_SLAVE_FINISH); + transfer_in_progress = false; + } +} + +static void __not_in_flash_func(i2c_slave_irq_handler)(void) +{ + i2c_hw_t *hw = i2c_get_hw(I2C_SLAVE_INSTANCE); + + uint32_t intr_stat = hw->intr_stat; + + if(intr_stat == 0) + { + return; + } + + if(intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) + { + hw->clr_tx_abrt; + finish_transfer(); + } + + if(intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) + { + hw->clr_start_det; + finish_transfer(); + } + + if(intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) + { + hw->clr_stop_det; + i2c_slave_buffer_handler(I2C_SLAVE_RECEIVE); + finish_transfer(); + } + + if(intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) + { + transfer_in_progress = true; + i2c_slave_buffer_handler(I2C_SLAVE_RECEIVE); + } + + if(intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) + { + hw->clr_rd_req; + transfer_in_progress = true; + i2c_slave_buffer_handler(I2C_SLAVE_REQUEST); + } +} + +void i2c_slave_init(void) +{ + // Init GPIO pins + gpio_set_function(I2C_SLAVE_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SLAVE_SCL_PIN, GPIO_FUNC_I2C); + + // Note: The I2C slave does clock stretching implicitly after a RD_REQ, while the Tx FIFO is empty. + // There is also an option to enable clock stretching while the Rx FIFO is full, but we leave it + // disabled since the Rx FIFO should never fill up (unless i2c_slave.handler() is way too slow). + i2c_set_slave_mode(I2C_SLAVE_INSTANCE, true, I2C_SLAVE_ADDRESS); + + i2c_hw_t *hw = i2c_get_hw(I2C_SLAVE_INSTANCE); + // unmask necessary interrupts + hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS | I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS; + + // enable interrupt for current core + uint num = I2C0_IRQ + i2c_get_index(I2C_SLAVE_INSTANCE); + irq_set_exclusive_handler(num, i2c_slave_irq_handler); + irq_set_enabled(num, true); +} + +void i2c_slave_deinit(void) +{ + uint num = I2C0_IRQ + i2c_get_index(I2C_SLAVE_INSTANCE); + irq_set_enabled(num, false); + irq_remove_handler(num, i2c_slave_irq_handler); + + i2c_set_slave_mode(I2C_SLAVE_INSTANCE, false, 0); + + transfer_in_progress = false; + + i2c_hw_t *hw = i2c_get_hw(I2C_SLAVE_INSTANCE); + hw->intr_mask = I2C_IC_INTR_MASK_RESET; +} diff --git a/slave/src/include/i2c_buffer.h b/slave/src/include/i2c_buffer.h new file mode 100644 index 0000000..8bfb859 --- /dev/null +++ b/slave/src/include/i2c_buffer.h @@ -0,0 +1,20 @@ +#ifndef I2C_BUFFER_H +#define I2C_BUFFER_H + +#include +#include "i2c_slave.h" + +typedef struct i2c_buffer_t { + uint8_t buffer[256]; + uint8_t buffer_reg; + bool buffer_reg_written; +} i2c_buffer_t; + +// I2c slave buffer handler for writing and reading data to the buffer +void __not_in_flash_func(i2c_slave_buffer_handler)(i2c_slave_event_t event); +// Update motors from the data in the i2c buffer +void update_motors_from_buffer(void); +// Update servo motors from the data in the i2c buffer +void update_servo_motors_from_buffer(void); + +#endif // I2C_BUFFER_H \ No newline at end of file diff --git a/slave/src/include/i2c_slave.h b/slave/src/include/i2c_slave.h new file mode 100644 index 0000000..49681e4 --- /dev/null +++ b/slave/src/include/i2c_slave.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Valentin Milea + * + * SPDX-License-Identifier: MIT + */ + +#ifndef I2C_SLAVE_H +#define I2C_SLAVE_H + +#include + +#define I2C_SLAVE_SDA_PIN 16 +#define I2C_SLAVE_SCL_PIN 17 +#define I2C_SLAVE_INSTANCE i2c0 +#define I2C_SLAVE_ADDRESS 0x09 + +typedef enum i2c_slave_event_t { + I2C_SLAVE_RECEIVE, // < Data from master is available for reading. Slave must read from Rx FIFO. + I2C_SLAVE_REQUEST, // < Master is requesting data. Slave must write into Tx FIFO. + I2C_SLAVE_FINISH, // < Master has sent a Stop or Restart signal. Slave may prepare for the next transfer. +} i2c_slave_event_t; + +static inline uint8_t i2c_slave_read_byte(void) +{ + i2c_hw_t *hw = i2c_get_hw(I2C_SLAVE_INSTANCE); + assert(hw->status & I2C_IC_STATUS_RFNE_BITS); // Rx FIFO must not be empty + return (uint8_t)hw->data_cmd; +} + +static inline void i2c_slave_write_byte(uint8_t value) +{ + i2c_hw_t *hw = i2c_get_hw(I2C_SLAVE_INSTANCE); + assert(hw->status & I2C_IC_STATUS_TFNF_BITS); // Tx FIFO must not be full + hw->data_cmd = value; +} + +// Init I2C with default parameters +void i2c_slave_init(void); + +// Deinit I2C with default parameters +void i2c_slave_deinit(void); + +#endif // I2C_SLAVE_H \ No newline at end of file diff --git a/slave/src/main.c b/slave/src/main.c new file mode 100644 index 0000000..424439e --- /dev/null +++ b/slave/src/main.c @@ -0,0 +1,43 @@ +/* *\ + Copyrights 2025 + Riombotique +\* */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Code du RPI Pico gérant les different Actionneurs. * +* Ce Pico est un esclave piloté par le Pico Principal. * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include "include/i2c_slave.h" +#include "include/i2c_buffer.h" +#include + +i2c_buffer_t i2c_buffer; + +int main(void) +{ + stdio_init_all(); + + sleep_ms(1000); + + puts("STDIO INIT ALL DONE"); + + i2c_slave_init(); + + puts("SLAVE INIT DONE"); + + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + puts("DEFAULT LED INIT DONE"); + + while(true) + { + gpio_put(PICO_DEFAULT_LED_PIN, 0); + printf("BUFFER[0]:%d\n", i2c_buffer.buffer[0]); + sleep_ms(300); + } + + return 0; +}