bluepill firmware that turns it into a USB-I2C multimaster gateway with a simple serial protocol.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
bluepill-i2c-gw/Core/Src/i2c_gateway.c

181 lines
5.0 KiB

#include <stdint.h>
#include <stdio.h>
#include "main.h"
#include "usbd_cdc_if.h"
#define I2C_DEBUG 0
#define I2C_MTU 512
#define HAL_CHECK(x) do { \
int rv = (x); \
if (rv != HAL_OK) { \
printf("HAL ERR %d at %s:%d\r\n", rv, __FILE__, __LINE__); \
}\
} while (0)
/**
* Packet format:
*
* Transmit packets
* LEN_MSB LEN_LSB ADDR Bytes...
*
* LEN does not include the length field itself!
*
* Command packets are sent as ADDR=255
* 0xFF CMD ARGs...
* - Set address: 0x01 ADDR
* - Set speed: 0x02 Speed TODO!!
* - Speed 1 ... 100kHz
* - Speed 4 ... 400kHz
*/
#define COMMAND_ADDRESS 0xFF
#define CMD_SET_ADDR 1 // the next param is the address, without shifting (i.e. 1-127)
#define CMD_SET_SPEED 2 // speed is 1 or 4 (100khz, 400khz)
enum FrameState {
FRAME_LEN_MSB,
FRAME_LEN_LSB,
FRAME_PAYLOAD,
};
static uint16_t framelen = 0;
static uint8_t i2c_recbuf[I2C_MTU + 3] = {};
static size_t recbuf_wp = 0;
static size_t framestate = FRAME_LEN_MSB;
static uint32_t last_rx_tick;
#define CMD_TIMEOUT_TICKS 500
static void handle_pkt(uint8_t *packet, size_t len);
static void handle_control_pkt(uint8_t *packet, size_t len);
void i2c_command(uint8_t *packet, size_t len)
{
uint32_t now = HAL_GetTick();
if (framestate != FRAME_LEN_MSB && (now - last_rx_tick) > CMD_TIMEOUT_TICKS) {
framestate = FRAME_LEN_MSB;
}
last_rx_tick = now;
for (int i=0; i<len;i++) {
uint8_t b = packet[i];
switch (framestate) {
case FRAME_LEN_MSB:
framelen = ((uint16_t)b) << 8;
framestate = FRAME_LEN_LSB;
break;
case FRAME_LEN_LSB:
framelen |= (uint16_t)b;
if (framelen == 0) {
framestate = FRAME_LEN_MSB;
} else {
framestate = FRAME_PAYLOAD;
recbuf_wp = 0;
}
break;
case FRAME_PAYLOAD:
i2c_recbuf[recbuf_wp++] = b;
if (recbuf_wp >= framelen) {
handle_pkt(i2c_recbuf, framelen);
framestate = FRAME_LEN_MSB;
}
break;
}
}
}
static void handle_pkt(uint8_t *packet, size_t len)
{
#if I2C_DEBUG
printf("Handle packet (%d bytes): ", len);
for(int i=0; i<len;i++) {
printf("%02x ", packet[i]);
}
printf("\r\n");
#endif
if (packet[0] == COMMAND_ADDRESS) {
printf("ctl: ");
// This is an in-band control packet
handle_control_pkt(packet, len);
} else {
uint8_t dest = packet[0];
#if I2C_DEBUG
printf("Tx(%d)->%d\r\n", len - 1, dest);
#endif
// Blocking transfer
HAL_CHECK(HAL_I2C_DisableListen_IT(&hi2c1));
HAL_CHECK(HAL_I2C_Master_Transmit(&hi2c1, (dest & 0x7F) << 1, &packet[1], len - 1, 100));
HAL_CHECK(HAL_I2C_EnableListen_IT(&hi2c1));
}
}
static void handle_control_pkt(uint8_t *packet, size_t len)
{
uint8_t cmd = packet[1];
uint8_t arg;
switch (cmd) {
case CMD_SET_ADDR:
if (len < 3) return;
arg = packet[2];
printf("Addr=0x%02x\r\n", arg);
MODIFY_REG(hi2c1.Instance->OAR1,
(I2C_OAR1_ADDMODE | I2C_OAR1_ADD8_9 | I2C_OAR1_ADD1_7 | I2C_OAR1_ADD0),
(I2C_ADDRESSINGMODE_7BIT | ((arg & 0x7F) << 1) ));
HAL_CHECK(HAL_I2C_EnableListen_IT(&hi2c1));
break;
default:
printf("bad cmd!\r\n");
}
}
/**
* @brief Slave Address Match callback.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param TransferDirection Master request Transfer Direction (Write/Read), value of @ref I2C_XferDirection_definition
* @param AddrMatchCode Address Match Code
* @retval None
*/
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
HAL_CHECK(HAL_I2C_Slave_Seq_Receive_IT(hi2c, i2c_recbuf, I2C_MTU, I2C_FIRST_AND_LAST_FRAME));
}
/**
* @brief Slave Rx Transfer completed callback.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @retval None
*/
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
int numBytes = I2C_MTU - hi2c->XferCount;
#if I2C_DEBUG
printf("rxd %d\r\n", numBytes);
#endif
UserTxBufferFS[0] = (numBytes >> 8) & 0xFF;
UserTxBufferFS[1] = numBytes & 0xFF;
memcpy(&UserTxBufferFS[2], i2c_recbuf, numBytes);
CDC_Transmit_FS(UserTxBufferFS, numBytes + 2);
}
/**
* @brief Listen Complete callback.
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @retval None
*/
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
// Start again
HAL_CHECK(HAL_I2C_EnableListen_IT(hi2c));
}