Skip to content
Merged
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
104 changes: 104 additions & 0 deletions source/board/steami/steami32.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Here's a table summarizing the commands available via I2C, their description, pa
| Clear Flash | 0x10 | _NONE_ | _NONE_ | Erase file content. |
| Write data | 0x11 | 1 + 30 bytes | _NONE_ | Append data to file. The first byte is the number of data to add. |
| Read sector | 0x20 | 2 bytes | 256 bytes | Read a sector (the parameters must be between 0-32768) |
| Write config | 0x30 | 31 bytes | _NONE_ | Write to internal flash config zone. Bytes 0-1 = offset, byte 2 = data length (max 28), bytes 3-30 = data (zero-padded). Total is always 31 bytes. |
| Read config | 0x31 | 2 bytes | 256 bytes | Read 256 bytes from internal flash config zone at the given offset. |
| Clear config | 0x32 | _NONE_ | _NONE_ | Erase the entire config zone (1 KB). |
| Status Register | 0x80 | _NONE_ | 1 byte | Get the status register (see below) |
| Error Register | 0x81 | _NONE_ | 1 byte | Get the error register (see below) |

Expand Down Expand Up @@ -91,6 +94,7 @@ The error register provides information on errors encountered by the device.
#include "steami_led.h"
#include "w25q64.h"
#include "steami_flash.h"
#include "steami_config.h"
#include "error_status.h"

#include <stdlib.h>
Expand All @@ -112,6 +116,10 @@ typedef enum {

TASK_READ_SECTOR,

TASK_READ_CONFIG,
TASK_WRITE_CONFIG,
TASK_CLEAR_CONFIG,

TASK_WAIT_FLASH_BUSY,
} steami_task;

Expand Down Expand Up @@ -205,6 +213,45 @@ static void on_I2C_receive_command(steami_i2c_command cmd, uint8_t* rx, uint16_t
break;
}

case READ_CONFIG:{
if(is_busy()){
steami_uart_write_string("ERROR I2C busy.\n");
break;
}

current_task = TASK_READ_CONFIG;
memcpy(task_rx, rx, rx_len);
task_rx_len = rx_len;
break;
}

case WRITE_CONFIG:{
if(is_busy()){
steami_uart_write_string("ERROR I2C busy.\n");
break;
}

if(rx_len < 3){
error_status_bad_parameter(&status_error);
break;
}

current_task = TASK_WRITE_CONFIG;
memcpy(task_rx, rx, rx_len);
task_rx_len = rx_len;
break;
}

case CLEAR_CONFIG:{
if(is_busy()){
steami_uart_write_string("ERROR I2C busy.\n");
break;
}

current_task = TASK_CLEAR_CONFIG;
break;
}

case STATUS:{
uint8_t status = 0x00;

Expand Down Expand Up @@ -394,6 +441,63 @@ void process_task()
}


case TASK_READ_CONFIG:{
if( task_rx_len == 2 ){
uint16_t offset = ((uint16_t)task_rx[0] << 8) | task_rx[1];
if( steami_config_read(offset, buffer_sector, 256) ){
steami_i2c_set_tx_data(buffer_sector, 256);
}
else{
error_status_set_last_command_fail(&status_error);
steami_uart_write_string("ERROR Unable to read config (bad offset)\n");
}
}
else{
error_status_bad_parameter(&status_error);
steami_uart_write_string("ERROR READ_CONFIG expects 2 bytes (offset)\n");
}

current_task = TASK_NONE;
break;
}

case TASK_WRITE_CONFIG:{
uint16_t offset = ((uint16_t)task_rx[0] << 8) | task_rx[1];
uint8_t data_len = task_rx[2];
uint8_t max_payload = task_rx_len - 3;

if( data_len > max_payload ){
error_status_bad_parameter(&status_error);
steami_uart_write_string("ERROR WRITE_CONFIG data_len exceeds payload\n");
current_task = TASK_NONE;
break;
}

if( steami_config_write(offset, task_rx + 3, data_len) ){
steami_uart_write_string("Config written OK\n");
}
else{
error_status_set_last_command_fail(&status_error);
steami_uart_write_string("ERROR Unable to write config\n");
}

current_task = TASK_NONE;
break;
}

case TASK_CLEAR_CONFIG:{
if( steami_config_erase() ){
steami_uart_write_string("Config erased OK\n");
}
else{
error_status_set_last_command_fail(&status_error);
steami_uart_write_string("ERROR Unable to erase config\n");
}

current_task = TASK_NONE;
break;
}

case TASK_WAIT_FLASH_BUSY:
if( !steami_flash_is_busy() ){
current_task = TASK_NONE;
Expand Down
92 changes: 92 additions & 0 deletions source/board/steami/steami_config.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @file steami_config.c
* @brief STeaMi board config zone in internal flash (F103 gap area)
*/

#include "steami_config.h"
#include "stm32f1xx.h"
#include <string.h>

/** Shadow buffer used during read-modify-write cycles. */
static uint8_t shadow[STEAMI_CONFIG_SIZE] __attribute__((aligned(4)));

bool steami_config_read(uint16_t offset, uint8_t *buf, uint16_t len)
{
if ((uint32_t)offset + len > STEAMI_CONFIG_SIZE) {
return false;
}

/* Config zone is memory-mapped — direct read. */
memcpy(buf, (const uint8_t *)(STEAMI_CONFIG_ADDR + offset), len);
return true;
}

bool steami_config_write(uint16_t offset, const uint8_t *data, uint16_t len)
{
if ((uint32_t)offset + len > STEAMI_CONFIG_SIZE) {
return false;
}

/* 1. Read current page into shadow buffer. */
memcpy(shadow, (const uint8_t *)STEAMI_CONFIG_ADDR, STEAMI_CONFIG_SIZE);

/* 2. Merge new data into shadow. */
memcpy(shadow + offset, data, len);

/* 3. Erase the page. */
FLASH_EraseInitTypeDef erase;
uint32_t error;

HAL_FLASH_Unlock();

memset(&erase, 0, sizeof(erase));
erase.TypeErase = FLASH_TYPEERASE_PAGES;
erase.PageAddress = STEAMI_CONFIG_ADDR;
erase.NbPages = 1;

if (HAL_FLASHEx_Erase(&erase, &error) != HAL_OK) {
HAL_FLASH_Lock();
return false;
}

/* 4. Program shadow buffer back (word-by-word). */
for (uint32_t i = 0; i < STEAMI_CONFIG_SIZE; i += 4) {
uint32_t word;
memcpy(&word, shadow + i, 4);

/* Skip words that are already erased (0xFFFFFFFF). */
if (word == 0xFFFFFFFF) {
continue;
}

if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
STEAMI_CONFIG_ADDR + i, word) != HAL_OK) {
HAL_FLASH_Lock();
return false;
}
}

HAL_FLASH_Lock();
return true;
}

bool steami_config_erase(void)
{
FLASH_EraseInitTypeDef erase;
uint32_t error;

HAL_FLASH_Unlock();

memset(&erase, 0, sizeof(erase));
erase.TypeErase = FLASH_TYPEERASE_PAGES;
erase.PageAddress = STEAMI_CONFIG_ADDR;
erase.NbPages = 1;

if (HAL_FLASHEx_Erase(&erase, &error) != HAL_OK) {
HAL_FLASH_Lock();
return false;
}

HAL_FLASH_Lock();
return true;
}
58 changes: 58 additions & 0 deletions source/board/steami/steami_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @file steami_config.h
* @brief STeaMi board config zone in internal flash (F103 gap area)
*
* The config zone occupies the 1 KB gap between the bootloader and the
* interface firmware (0x0800BC00 - 0x0800BFFF). This area survives
* interface firmware updates, making it suitable for factory-programmed
* data such as board revision, name, and sensor calibration.
Comment thread
nedseb marked this conversation as resolved.
*/

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "daplink_addr.h"
#include "compiler.h"

/** Start address of the config zone (BL/IF gap). */
#define STEAMI_CONFIG_ADDR (DAPLINK_ROM_BL_START + DAPLINK_ROM_BL_SIZE)

/** Size of the config zone in bytes (one flash page). */
#define STEAMI_CONFIG_SIZE DAPLINK_SECTOR_SIZE

/* Compile-time check: config zone must fit between bootloader and interface. */
COMPILER_ASSERT(STEAMI_CONFIG_ADDR + STEAMI_CONFIG_SIZE <= DAPLINK_ROM_IF_START);

/**
* @brief Read data from the config zone.
*
* The config zone is memory-mapped, so this is a simple memcpy.
*
* @param offset Byte offset within the config zone (0 .. STEAMI_CONFIG_SIZE-1)
* @param buf Destination buffer
* @param len Number of bytes to read
* @return true on success, false if offset+len exceeds the zone
*/
bool steami_config_read(uint16_t offset, uint8_t *buf, uint16_t len);

/**
* @brief Write data to the config zone.
*
* Erases the entire 1 KB page then programs the supplied data.
* A shadow buffer is used to preserve existing content outside
* the written range.
*
* @param offset Byte offset within the config zone
* @param data Source data
* @param len Number of bytes to write
* @return true on success, false on parameter error or flash failure
*/
bool steami_config_write(uint16_t offset, const uint8_t *data, uint16_t len);

/**
* @brief Erase the entire config zone (fill with 0xFF).
*
* @return true on success, false on flash erase failure
*/
bool steami_config_erase(void);
12 changes: 12 additions & 0 deletions source/board/steami/steami_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ static bool is_command_valid(uint8_t cmd){
case GET_FILENAME:
case WRITE_DATA:
case READ_SECTOR:
case WRITE_CONFIG:
case READ_CONFIG:
case CLEAR_CONFIG:
case STATUS:
case ERROR_STATUS:
return true;
Expand Down Expand Up @@ -65,6 +68,15 @@ static uint16_t get_argument_byte_number(uint8_t cmd){
case READ_SECTOR:
return 2;

case WRITE_CONFIG:
return 31;

case READ_CONFIG:
return 2;

case CLEAR_CONFIG:
return 0;

case STATUS:
return 0;

Expand Down
6 changes: 5 additions & 1 deletion source/board/steami/steami_i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ typedef enum {
WRITE_DATA = 0x11,

READ_SECTOR = 0x20,


WRITE_CONFIG = 0x30,
READ_CONFIG = 0x31,
CLEAR_CONFIG = 0x32,

STATUS = 0x80,
ERROR_STATUS = 0x81,
} steami_i2c_command;
Expand Down