Skip to content

Commit 2024a09

Browse files
authored
Serial and I2C fixes for Microbit V2 (#892)
-Enabled UART HW overrun interrupt -Optimized time spent in I2C IRQ handler by moving bufffer clearing outside -Add I2C busy singaling mechanism -Add I2C ASCII encoding window command feature
1 parent f5fb4b9 commit 2024a09

File tree

4 files changed

+110
-18
lines changed

4 files changed

+110
-18
lines changed

source/board/microbitv2/i2c_commands.c

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ static volatile bool g_SlaveCompletionFlag = false;
4747
static volatile bool g_SlaveRxFlag = false;
4848
static uint8_t address_match = 0;
4949
static uint32_t transferredCount = 0;
50+
static const uint8_t g_slave_busy_error_buff[] = {gErrorResponse_c, gErrorBusy_c};
51+
static uint8_t g_slave_TX_buff_ready = 0;
52+
static uint8_t g_slave_busy = 0;
5053

5154
static i2cWriteCallback_t pfWriteCommsCallback = NULL;
5255
static i2cReadCallback_t pfReadCommsCallback = NULL;
@@ -73,6 +76,8 @@ static void i2c_write_flash_callback(uint8_t* pData, uint8_t size);
7376
static void i2c_read_flash_callback(uint8_t* pData, uint8_t size);
7477

7578
static void i2c_slave_callback(I2C_Type *base, i2c_slave_transfer_t *xfer, void *userData) {
79+
i2cCommand_t i2cResponse = {0};
80+
7681
switch (xfer->event)
7782
{
7883
/* Address match event */
@@ -86,21 +91,31 @@ static void i2c_slave_callback(I2C_Type *base, i2c_slave_transfer_t *xfer, void
8691
/* Transmit request */
8792
case kI2C_SlaveTransmitEvent:
8893
/* Update information for transmit process */
89-
xfer->data = g_slave_TX_buff;
90-
xfer->dataSize = I2C_DATA_LENGTH;
94+
// Send response if the buffer is ready, otherwise send busy error
95+
if (g_slave_TX_buff_ready) {
96+
xfer->data = g_slave_TX_buff;
97+
xfer->dataSize = I2C_DATA_LENGTH;
98+
} else {
99+
xfer->data = (uint8_t*)g_slave_busy_error_buff;
100+
xfer->dataSize = sizeof(g_slave_busy_error_buff);
101+
g_slave_busy = 1;
102+
}
91103
g_SlaveRxFlag = false;
92104
break;
93105

94106
/* Receive request */
95107
case kI2C_SlaveReceiveEvent:
96108
/* Update information for received process */
109+
// We don't need to clear g_slave_RX_buff because we also have the
110+
// transferredCount to know what data is valid
111+
xfer->data = g_slave_RX_buff;
112+
xfer->dataSize = I2C_DATA_LENGTH;
113+
97114
// Hack: Default driver can't differentiate between RX or TX on
98115
// completion event, so we set a flag here. Can't process more
99116
// than I2C_DATA_LENGTH bytes on RX
100-
memset(&g_slave_RX_buff, 0, sizeof(g_slave_RX_buff));
101-
xfer->data = g_slave_RX_buff;
102-
xfer->dataSize = I2C_DATA_LENGTH;
103117
g_SlaveRxFlag = true;
118+
104119
break;
105120

106121
/* Transfer done */
@@ -115,9 +130,20 @@ static void i2c_slave_callback(I2C_Type *base, i2c_slave_transfer_t *xfer, void
115130

116131
// Ignore NOP cmd in I2C Write
117132
if (!(g_SlaveRxFlag && g_slave_RX_buff[0] == 0x00)) {
118-
main_board_event();
133+
// Only process events if the busy error was not read
134+
if (!g_slave_busy) {
135+
main_board_event();
136+
} else {
137+
g_slave_busy = 0;
138+
}
119139
}
120140

141+
// Buffer with response is not ready after an I2C Write inside the IRQ
142+
// It will be ready when the task attends the I2C event
143+
if(g_SlaveRxFlag) {
144+
g_slave_TX_buff_ready = 0;
145+
}
146+
121147
i2c_allow_sleep = false;
122148
break;
123149

@@ -130,6 +156,7 @@ static void i2c_slave_callback(I2C_Type *base, i2c_slave_transfer_t *xfer, void
130156
void board_custom_event() {
131157

132158
if (g_SlaveRxFlag) {
159+
i2c_clearBuffer();
133160
if (pfWriteCommsCallback && address_match == I2C_SLAVE_NRF_KL_COMMS) {
134161
pfWriteCommsCallback(&g_slave_RX_buff[0], transferredCount);
135162
}
@@ -259,6 +286,7 @@ void i2c_fillBuffer (uint8_t* data, uint32_t position, uint32_t size) {
259286
for (uint32_t i = 0; i < size; i++) {
260287
g_slave_TX_buff[position + i] = data[i];
261288
}
289+
g_slave_TX_buff_ready = 1;
262290
}
263291

264292
static void i2c_write_comms_callback(uint8_t* pData, uint8_t size) {
@@ -276,7 +304,7 @@ static void i2c_write_comms_callback(uint8_t* pData, uint8_t size) {
276304
memcpy(&i2cResponse.cmdData.readRspCmd.data, &board_id_hex, sizeof(board_id_hex));
277305
break;
278306
case gI2CProtocolVersion_c: {
279-
uint16_t i2c_version = 1;
307+
uint16_t i2c_version = 2;
280308
i2cResponse.cmdData.readRspCmd.dataSize = sizeof(i2c_version);
281309
memcpy(&i2cResponse.cmdData.readRspCmd.data, &i2c_version, sizeof(i2c_version));
282310
}
@@ -413,8 +441,6 @@ static void i2c_read_comms_callback(uint8_t* pData, uint8_t size) {
413441
break;
414442
}
415443

416-
i2c_clearBuffer();
417-
418444
// Release COMBINED_SENSOR_INT
419445
PORT_SetPinMux(COMBINED_SENSOR_INT_PORT, COMBINED_SENSOR_INT_PIN, kPORT_PinDisabledOrAnalog);
420446
}
@@ -478,8 +504,6 @@ static void i2c_write_flash_callback(uint8_t* pData, uint8_t size) {
478504
uint32_t address = storage_address + FLASH_STORAGE_ADDRESS;
479505
uint32_t length = __REV(pI2cCommand->cmdData.write.length);
480506
uint32_t data = (uint32_t) pI2cCommand->cmdData.write.data;
481-
482-
i2c_clearBuffer();
483507

484508
switch (pI2cCommand->cmdId) {
485509
case gFlashDataWrite_c:
@@ -606,6 +630,44 @@ static void i2c_write_flash_callback(uint8_t* pData, uint8_t size) {
606630
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
607631
}
608632
break;
633+
case gFlashCfgEncWindow_c:
634+
if (size == 1) {
635+
/* If size is 1 (only cmd id), this means it's a read */
636+
uint32_t tempFileEncWindowStart = __REV(gflashConfig.fileEncWindowStart);
637+
uint32_t tempFileEncWindowEnd = __REV(gflashConfig.fileEncWindowEnd);
638+
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
639+
i2c_fillBuffer((uint8_t*) &tempFileEncWindowStart, 1, sizeof(gflashConfig.fileEncWindowStart));
640+
i2c_fillBuffer((uint8_t*) &tempFileEncWindowEnd, 5, sizeof(gflashConfig.fileEncWindowEnd));
641+
} else if (size == 9) {
642+
/* If size is 9 (cmd id + 8B data), this means it's a write */
643+
uint32_t tempFileEncWindowStart = pI2cCommand->cmdData.data[0] << 24 |
644+
pI2cCommand->cmdData.data[1] << 16 |
645+
pI2cCommand->cmdData.data[2] << 8 |
646+
pI2cCommand->cmdData.data[3] << 0;
647+
uint32_t tempFileEncWindowEnd = pI2cCommand->cmdData.data[4] << 24 |
648+
pI2cCommand->cmdData.data[5] << 16 |
649+
pI2cCommand->cmdData.data[6] << 8 |
650+
pI2cCommand->cmdData.data[7] << 0;
651+
652+
/* Validate encoding window */
653+
if (tempFileEncWindowStart <= tempFileEncWindowEnd) {
654+
gflashConfig.fileEncWindowStart = tempFileEncWindowStart;
655+
tempFileEncWindowStart = __REV(gflashConfig.fileEncWindowStart);
656+
gflashConfig.fileEncWindowEnd = tempFileEncWindowEnd;
657+
tempFileEncWindowEnd = __REV(gflashConfig.fileEncWindowEnd);
658+
659+
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
660+
i2c_fillBuffer((uint8_t*) &tempFileEncWindowStart, 1, sizeof(gflashConfig.fileEncWindowStart));
661+
i2c_fillBuffer((uint8_t*) &tempFileEncWindowEnd, 5, sizeof(gflashConfig.fileEncWindowEnd));
662+
} else {
663+
pI2cCommand->cmdId = gFlashError_c;
664+
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
665+
}
666+
} else {
667+
pI2cCommand->cmdId = gFlashError_c;
668+
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
669+
}
670+
break;
609671
case gFlashCfgFileVisible_c:
610672
if (size == 1) {
611673
/* If size is 1 (only cmd id), this means it's a read */
@@ -649,6 +711,8 @@ static void i2c_write_flash_callback(uint8_t* pData, uint8_t size) {
649711
memcpy(gflashConfig.fileName, FLASH_CFG_FILENAME, 11);
650712
gflashConfig.fileSize = FLASH_CFG_FILESIZE;
651713
gflashConfig.fileVisible = FLASH_CFG_FILEVISIBLE;
714+
gflashConfig.fileEncWindowStart = 0;
715+
gflashConfig.fileEncWindowEnd = 0;
652716
}
653717
i2c_fillBuffer((uint8_t*) pI2cCommand, 0, 1);
654718
break;
@@ -676,8 +740,6 @@ static void i2c_write_flash_callback(uint8_t* pData, uint8_t size) {
676740
}
677741

678742
static void i2c_read_flash_callback(uint8_t* pData, uint8_t size) {
679-
i2c_clearBuffer();
680-
681743
// Release COMBINED_SENSOR_INT
682744
PORT_SetPinMux(COMBINED_SENSOR_INT_PORT, COMBINED_SENSOR_INT_PIN, kPORT_PinDisabledOrAnalog);
683745
}

source/board/microbitv2/i2c_commands.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ typedef enum errorCode_tag {
7777
gErrorWrongPropertySize_c = 0x35,
7878
gErrorReadDisallowed_c = 0x36,
7979
gErrorWriteDisallowed_c = 0x37,
80-
gErrorWriteFail_c = 0x38
80+
gErrorWriteFail_c = 0x38,
81+
gErrorBusy_c = 0x39
8182
} errorCode_t;
8283

8384
typedef __PACKED_STRUCT readReqCmd_tag {
@@ -123,6 +124,8 @@ typedef __PACKED_STRUCT flashConfig_tag {
123124
vfs_filename_t fileName;
124125
uint32_t fileSize;
125126
bool fileVisible;
127+
uint32_t fileEncWindowStart;
128+
uint32_t fileEncWindowEnd;
126129
} flashConfig_t;
127130

128131
/*! Flash interface command type */
@@ -135,6 +138,7 @@ typedef enum flashCmdId_tag {
135138
gFlashStorageSize_c = 0x06,
136139
gFlashSectorSize_c = 0x07,
137140
gFlashRemountMSD_c = 0x08,
141+
gFlashCfgEncWindow_c = 0x09,
138142
gFlashDataRead_c = 0x0A,
139143
gFlashDataWrite_c = 0x0B,
140144
gFlashDataErase_c = 0x0C,

source/board/microbitv2/microbitv2.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ flashConfig_t gflashConfig = {
7171
.fileName = FLASH_CFG_FILENAME,
7272
.fileSize = FLASH_CFG_FILESIZE,
7373
.fileVisible = FLASH_CFG_FILEVISIBLE,
74+
.fileEncWindowStart = 0,
75+
.fileEncWindowEnd = 0,
7476
};
7577

7678
typedef enum {
@@ -527,7 +529,8 @@ void vfs_user_build_filesystem_hook() {
527529
}
528530
}
529531

530-
file_size = gflashConfig.fileSize;
532+
// Add encoding window file size. 1B encoded into 2B ASCII
533+
file_size = gflashConfig.fileSize + (gflashConfig.fileEncWindowEnd - gflashConfig.fileEncWindowStart);
531534

532535
if (gflashConfig.fileVisible) {
533536
vfs_create_file(gflashConfig.fileName, read_file_data_txt, 0, file_size);
@@ -537,9 +540,32 @@ void vfs_user_build_filesystem_hook() {
537540
// File callback to be used with vfs_add_file to return file contents
538541
static uint32_t read_file_data_txt(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
539542
{
543+
uint32_t read_address = FLASH_STORAGE_ADDRESS + (VFS_SECTOR_SIZE * sector_offset);
544+
uint32_t encoded_data_offset = (gflashConfig.fileEncWindowEnd - gflashConfig.fileEncWindowStart);
545+
540546
// Ignore out of bound reads
541-
if ( (FLASH_STORAGE_ADDRESS + VFS_SECTOR_SIZE * sector_offset) < (FLASH_CONFIG_ADDRESS + FLASH_INTERFACE_SIZE) ) {
542-
memcpy(data, (uint8_t *) (FLASH_STORAGE_ADDRESS + VFS_SECTOR_SIZE * sector_offset), VFS_SECTOR_SIZE);
547+
if ( read_address < (FLASH_CONFIG_ADDRESS + FLASH_INTERFACE_SIZE + encoded_data_offset) ) {
548+
for (uint32_t i = 0; i < VFS_SECTOR_SIZE; i++) {
549+
if (i + (VFS_SECTOR_SIZE * sector_offset) < gflashConfig.fileEncWindowStart) {
550+
// If data is before encoding window, no offset is needed
551+
data[i] = *(uint8_t *) (read_address + i);
552+
} else if(i + (VFS_SECTOR_SIZE * sector_offset) < (gflashConfig.fileEncWindowStart + encoded_data_offset * 2)) {
553+
// Data inside encoding window needs to consider encoding window start and size
554+
uint8_t enc_byte = *(uint8_t *) (FLASH_STORAGE_ADDRESS + ((VFS_SECTOR_SIZE * sector_offset) + gflashConfig.fileEncWindowStart + i ) / 2);
555+
if (i % 2 == 0) {
556+
// High nibble
557+
enc_byte = 0x0F & (enc_byte >> 4);
558+
} else {
559+
// Low nibble
560+
enc_byte = 0x0F & enc_byte;
561+
}
562+
// Encode one nibble to one ASCII byte
563+
data[i] = enc_byte <= 9 ? enc_byte + 0x30 : enc_byte + 0x37;
564+
} else {
565+
// If data is after encoding window, adjustment is needed
566+
data[i] = *(uint8_t *) (read_address + i - encoded_data_offset);
567+
}
568+
}
543569
}
544570

545571
return VFS_SECTOR_SIZE;

source/hic_hal/freescale/kl27z/uart.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ int32_t uart_set_configuration(UART_Configuration *config)
180180
// Enable UART interrupt
181181
NVIC_ClearPendingIRQ(UART_RX_TX_IRQn);
182182
NVIC_EnableIRQ(UART_RX_TX_IRQn);
183-
UART->CTRL |= LPUART_CTRL_RIE_MASK;
183+
UART->CTRL |= LPUART_CTRL_RIE_MASK | LPUART_CTRL_ORIE_MASK;
184184
return 1;
185185
}
186186

0 commit comments

Comments
 (0)