diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/CMakeLists.txt | 5 | ||||
-rw-r--r-- | main/config.def.h | 20 | ||||
-rw-r--r-- | main/i2c.c | 84 | ||||
-rw-r--r-- | main/i2c.h | 5 | ||||
-rw-r--r-- | main/init.c | 7 | ||||
-rw-r--r-- | main/main.c | 7 | ||||
-rw-r--r-- | main/mod.c | 5 | ||||
-rw-r--r-- | main/pbdrv.c | 95 | ||||
-rw-r--r-- | main/pbdrv.h | 52 | ||||
-rw-r--r-- | main/sock.c | 29 | ||||
-rw-r--r-- | main/sock.h | 3 |
11 files changed, 218 insertions, 94 deletions
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7a0b136..d6cbf33 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -10,6 +10,7 @@ include(lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_impor add_subdirectory(lib/mpack) add_subdirectory(lib/i2ctcp) add_subdirectory(lib/pbdrv) +include(lib/pbdrv/ext/stdlib/include.cmake) # pico-stdlib is compatible with C stdlib project(puzzlebox_main C CXX ASM) @@ -23,18 +24,20 @@ add_executable(main mod.c tasks.c blink.c + pbdrv.c ) pico_enable_stdio_usb(main 1) pico_enable_stdio_uart(main 0) pico_add_extra_outputs(main) -include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include) +# include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include) target_include_directories(main PRIVATE .) target_link_libraries(main pico_cyw43_arch_lwip_sys_freertos pico_stdlib + pico_i2c_slave hardware_i2c FreeRTOS-Kernel FreeRTOS-Kernel-Heap4 diff --git a/main/config.def.h b/main/config.def.h index 3d325fe..e9503ed 100644 --- a/main/config.def.h +++ b/main/config.def.h @@ -56,11 +56,10 @@ #endif /** \} */ -#ifndef CFG_LED_PIN -//! status LED pin -#define CFG_LED_PIN CYW43_WL_GPIO_LED_PIN -#endif - +/** + * \name I2C configuration + * \{ + */ #ifndef CFG_SDA_PIN //! I^2^C SDA pin #define CFG_SDA_PIN 16 @@ -69,4 +68,15 @@ //! I^2^C SCL pin #define CFG_SCL_PIN 17 #endif +/** \} */ + +#ifndef CFG_LED_PIN +//! status LED pin +#define CFG_LED_PIN CYW43_WL_GPIO_LED_PIN +#endif + +#ifndef CFG_PB_MOD_MAX +//! maximum number of simultaniously connected puzzle modules +#define CFG_PB_MOD_MAX 8 +#endif @@ -3,69 +3,47 @@ #include <stdio.h> #include <stddef.h> #include <stdint.h> -#include <string.h> #include <pico/stdlib.h> #include <hardware/i2c.h> #include "i2c.h" #include "pb-mod.h" - -// uint8_t* scan_bus(uint8_t *array) { -// int ret; -// int i = 0; -// uint8_t rxdata; -// -// for(int addr = 0; addr < (1<<7); addr++) { -// // ignore reserved addresses -// // These are any addresses of the form 000 0xxx or 111 1xxx -// // ret = i2c_read_blocking(I2C_PORT, addr, &rxdata, 1, false); -// -// // if acknowledged -> ret == number of bytes sent -// if(ret > 0){ -// printf("found i2c slave on addr: %d\n", addr); -// array[i] = addr; -// i++; -// } -// } -// -// return array; -// } - -void pbdrv_i2c_recv(const uint8_t * a, size_t b) { - printf("%.*s", b, a); +#include "pbdrv.h" +#include "config.h" +#include "pb-buf.h" +#include "pb-send.h" + +i2c_addr_t modules[CFG_PB_MOD_MAX]; +size_t modules_size = 0; + +static void state_exchange() { + for (size_t i = 0; i < modules_size; i++) { + pb_buf_t buf = pb_send_state_req(); + + pb_buf_free(&buf); + } } void bus_task() { - // scan bus for slaves - // send updates at regular intervals - vTaskDelay(1000 / portTICK_PERIOD_MS); - - // int i = 0; - // uint8_t found[MAX_SLAVES]; - // init_addr_array(found, MAX_SLAVES); - - while (true) { - vTaskDelay(10 / portTICK_PERIOD_MS); - pbdrv_i2c_send(0x69, (uint8_t *) "bbbbbbbb", 9); - } + // do a scan of the bus + bus_scan(); - // while(1) { - // // printf("Bus scan!"); - // scan_bus(found); + // FIXME: this should be removed (see handover: RP2040 I2C limitations) + // wait for 5 seconds until all handshake responses are received + pb_mod_blocking_delay_ms(5e3); - // for(int i = 0; i < MAX_SLAVES; i++){ - // if( found[i] == 0x00 ) - // break; - // - // uint8_t data = 0x01; - // // send data to found slave address - // write_i2c(found[i], &data, 1); + while(1) { + // send my state to all puzzle modules + state_exchange(); + + // wait 1 second + pb_mod_blocking_delay_ms(1e3); + } +} - // data = 0x02; - // write_i2c(found[i], &data, 1); - // // request update from slave addr at found[i] - // //write_i2c(); - // } - // } +void pb_route_cmd_magic_res(pb_msg_t * msg) { + if (modules_size == CFG_PB_MOD_MAX) return; + modules[modules_size++] = msg->sender; + printf("i2c: registered puzzle module w/ address 0x%02x\n", msg->sender); } @@ -1,9 +1,4 @@ #pragma once -// https://github.com/raspberrypi/pico-examples/tree/master/i2c -// https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html - -#define MAX_SLAVES 10 -#define MAX_TIMEOUT_TIME 50 //ms //! looking for slave addresses and requesting updates void bus_task(); diff --git a/main/init.c b/main/init.c index bd00c04..6d29d19 100644 --- a/main/init.c +++ b/main/init.c @@ -7,7 +7,7 @@ #include "config.h" #include "init.h" #include "tasks.h" -#include "drv/rp2040/mod.h" +#include "pbdrv.h" static void init_stdio() { stdio_init_all(); @@ -32,7 +32,10 @@ static void init_i2c() { gpio_set_function(CFG_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(CFG_SCL_PIN, GPIO_FUNC_I2C); - pbdrv_setup(); + gpio_pull_up(CFG_SDA_PIN); + gpio_pull_up(CFG_SCL_PIN); + + pb_setup(); } static void async_init() { diff --git a/main/main.c b/main/main.c index 7c1bb6a..1c615fc 100644 --- a/main/main.c +++ b/main/main.c @@ -6,12 +6,5 @@ int main() { init(); vTaskStartScheduler(); - - while(1) { - // we should have never gotten here - printf("Why are we here?!\n"); - } - - return 0; } @@ -1,5 +1,6 @@ +#include "pb.h" #include "pb-mod.h" -const char * PBDRV_MOD_NAME = "main controller"; -const i2c_addr_t PBDRV_MOD_ADDR = 0x20; +const char * PB_MOD_NAME = "main controller"; +const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_MAIN; diff --git a/main/pbdrv.c b/main/pbdrv.c new file mode 100644 index 0000000..0624897 --- /dev/null +++ b/main/pbdrv.c @@ -0,0 +1,95 @@ +#include "pb.h" + +#include "pb.h" +#include "pb-types.h" +#include "pb-mod.h" +#include "pb-send.h" + +#include <hardware/i2c.h> +#include <hardware/gpio.h> +#include <pico/i2c_slave.h> +#include <pico/stdio.h> + +#include <FreeRTOS.h> +#include <stdio.h> +#include <timers.h> + +#define PB_I2C i2c0 +#define BUF_SIZE 256 +#define MSGS_MAX 4 + +typedef struct { + uint8_t data[BUF_SIZE]; + size_t size; +} i2c_msg_buf_t; + +static i2c_msg_buf_t msgs[MSGS_MAX]; +static size_t msg_index = 0; + +static void async_pb_i2c_recv(void * _msg, uint32_t _) { + i2c_msg_buf_t * msg = _msg; + pb_i2c_recv(msg->data, msg->size); +} + +static void msg_complete(i2c_msg_buf_t * msg) { + // defer pb_i2c_recv call to FreeRTOS scheduler as pb_i2c_recv takes + // too long to return from an ISR + xTimerPendFunctionCallFromISR(async_pb_i2c_recv, msg, 0, NULL); + + // prepare next message for use + msg_index = (msg_index + 1) % MSGS_MAX; + msgs[msg_index].size = 0; +} + +// This function is called from the I2C ISR +static void recv_event(i2c_inst_t *i2c, i2c_slave_event_t event) { + i2c_msg_buf_t * msg = &msgs[msg_index]; + + switch (event) { + case I2C_SLAVE_RECEIVE: { + if (msg->size == BUF_SIZE) return; + msg->data[msg->size++] = i2c_read_byte_raw(PB_I2C); + break; + } + case I2C_SLAVE_FINISH: { + msg_complete(msg); + break; + } + default: break; + } +} + +void pb_setup() { + i2c_init(PB_I2C, PB_CLOCK_SPEED_HZ); + i2c_slave_init(PB_I2C, PB_MOD_ADDR, &recv_event); +} + +__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { + i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR); + + // false to write stop condition to i2c bus + i2c_write_timeout_us(PB_I2C, addr, buf, sz, false, PB_TIMEOUT_US); + + i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR); +} + +void bus_scan() { + i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR); + + pb_buf_t buf = pb_send_magic_req(); + + // check for all 7-bit addresses + uint16_t addr_max = 1 << 7; + for (uint16_t addr = 0x00; addr < addr_max; addr++) { + i2c_write_timeout_us(PB_I2C, addr, (uint8_t *) buf.data, buf.size, false, PB_TIMEOUT_US); + } + + pb_buf_free(&buf); + + i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR); +} + +void pb_mod_blocking_delay_ms(unsigned long ms) { + vTaskDelay(ms / portTICK_PERIOD_MS); +} + diff --git a/main/pbdrv.h b/main/pbdrv.h new file mode 100644 index 0000000..a751000 --- /dev/null +++ b/main/pbdrv.h @@ -0,0 +1,52 @@ +#pragma once + +#include "pb-types.h" + +/** + * This is the RP2040 puzzle bus driver. This file is no longer inside + * lib/pb//rp2040 as it is tightly coupled to both the pico-sdk and + * freertos functions. I have tried to get FreeRTOS to play nicely with the + * CMake subproject layout, but the pico-sdk and the rp2040 port of freertos + * both rely on CMake's import() functionality, which makes using FreeRTOS in a + * libary context extremely messy. + * + * The workaround implemented in this driver was already kind of messy, and a + * different microcontroller should be used for the main controller instead. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +//! puzzle bus driver setup +void pb_setup(); + +/** + * While the RP2040's datasheet claims it supports multi-master configurations + * by implementing bus arbitration, it does not natively support a mode where + * it is configured as a (multi-)master with a slave address, such that it can + * be addressed by other multi-masters. This function includes a hacky + * workaround that teporarily sets the RP2040 to I2C master mode to send a + * message, and then restores it back to slave mode. + * + * This approach results in some received frames being (partially) dropped in + * the time period between the invocation of this function and the bus becoming + * idle (and the message is sent). + */ +void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz); + +/** + * \brief Scan the bus for I2C slaves, and send handshake messages to ACK-ing + * slaves. + * + * As a result of the RP2040 hardware limitations detailed at the top of this + * file, this function is also implemented in this file, even through it does + * not belong to the puzzle bus driver. In order to not miss any handshake + * responses, the bus should remain busy during the entire scan. + */ +void bus_scan(); + +#ifdef __cplusplus +} +#endif + diff --git a/main/sock.c b/main/sock.c index 5d75e8f..33a6111 100644 --- a/main/sock.c +++ b/main/sock.c @@ -9,10 +9,8 @@ #include "config.h" #include "i2ctcpv1.h" #include "sock.h" -#include "i2c.h" -#include <hardware/i2c.h> +#include "pb-mod.h" -extern uint8_t found[MAX_SLAVES]; struct netconn* current_connection = NULL; i2ctcp_msg_t recv_msg; @@ -37,31 +35,30 @@ void i2c_send(uint16_t addr, const char * data, size_t data_size) { free(buf); } -void i2c_recv(uint16_t addr, const char * data, size_t data_size) { - // TODO: this function should forward the recieved message onto the puzzle - // bus instead of printing/replying - - printf("Addr: %lu, Data: %c, Data_size: %lu\n", addr, data[0], data_size); - i2c_write_blocking(i2c0, addr, data, data_size, false); +static void i2c_recv_fwd(uint16_t addr, const uint8_t * data, size_t data_size) { + if (addr == PB_MOD_ADDR) { + // addressed to me = act as recieved + pb_i2c_recv(data, data_size); + } else { + // addressed to other puzzle module = forward + pb_i2c_send(addr, data, data_size); + } } void recv_handler(struct netconn* conn, struct netbuf* buf) { - // i2ctcp_read_reset(&recv_msg); + i2ctcp_read_reset(&recv_msg); do { char* data; uint16_t len; netbuf_data(buf, (void**)&data, &len); - printf("now scanning the bus!!!!\n"); - scan_bus(found); - // continue early if more data is needed to complete message - // if (i2ctcp_read(&recv_msg, data, len) > 0) continue; + if (i2ctcp_read(&recv_msg, data, len) != 0) continue; // forward received message to puzzle bus - // i2c_recv(recv_msg.addr, recv_msg.data, recv_msg.length); - // free(recv_msg.data); + i2c_recv_fwd(recv_msg.addr, (uint8_t *) recv_msg.data, recv_msg.length); + free(recv_msg.data); } while (netbuf_next(buf) >= 0); netbuf_delete(buf); diff --git a/main/sock.h b/main/sock.h index 61828fb..38fca01 100644 --- a/main/sock.h +++ b/main/sock.h @@ -6,6 +6,3 @@ //! start listening for TCP socket requests void serve_task(); -void i2c_send(uint16_t addr, const char * data, size_t data_size); -void i2c_recv(uint16_t addr, const char * data, size_t data_size); - |