Command handing works!

The command handler can now receive a command, then it can run a
function / return some data based on it.
This commit is contained in:
Sebastian H. Gabrielli 2024-04-16 15:30:38 +02:00
parent 8e3607cbda
commit 9fd877aab5
6 changed files with 122 additions and 65 deletions

View File

@ -1,9 +1,10 @@
#include "command-handler.h" #include "command-handler.h"
command_context_t parse_command(uint8_t *command, uint8_t command_len) { // Initialize empty, global command context
// Create the context volatile command_context_t context = {UNKNOWN_COMMAND, SRC_NONE, FAN_NONE,
command_context_t context; CNF_NONE};
void parse_command(uint8_t command[], uint8_t command_len) {
/////////////////////// ///////////////////////
// Command selection // // Command selection //
/////////////////////// ///////////////////////
@ -11,11 +12,12 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// Validate that we have a command // Validate that we have a command
if (command_len < 1) { if (command_len < 1) {
context.command = UNKNOWN_COMMAND; context.command = UNKNOWN_COMMAND;
return context;
} }
uint8_t foo = command[0];
// Figure out which command to run // Figure out which command to run
switch (command[0]) { switch (foo) {
case 0x11: // Read config case 0x11: // Read config
context.command = READ_CONFIG; context.command = READ_CONFIG;
break; break;
@ -48,14 +50,14 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// Check if the command does not require a parameter. If it does not, return. // Check if the command does not require a parameter. If it does not, return.
if (context.command == READ_TERMPERATURE) { if (context.command == READ_TERMPERATURE) {
return context; return;
} }
// Validate that we have a first parameter, else requirements for command are // Validate that we have a first parameter, else requirements for command are
// not fulfilled. return unknown command. // not fulfilled. return unknown command.
if (command_len < 2) { if (command_len < 2) {
context.command = UNKNOWN_COMMAND; context.command = UNKNOWN_COMMAND;
return context; return;
} }
// Store the parameter // Store the parameter
@ -109,14 +111,14 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// Check if the command does not require a second parameter. If it does not, // Check if the command does not require a second parameter. If it does not,
// return. Only config write requires a second parameter. // return. Only config write requires a second parameter.
if (context.command != WRITE_CONFIG) { if (context.command != WRITE_CONFIG) {
return context; return;
} }
// Validate that we have a first parameter, else requirements for command are // Validate that we have a first parameter, else requirements for command are
// not fulfilled. return unknown command. // not fulfilled. return unknown command.
if (command_len < 3) { if (command_len < 3) {
context.command = UNKNOWN_COMMAND; context.command = UNKNOWN_COMMAND;
return context; return;
} }
// Store the parameter // Store the parameter
@ -124,8 +126,32 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// TODO: Handle the config parameters // TODO: Handle the config parameters
// Return the context // exit
return context; return;
} }
void route_command(command_context_t command); uint8_t route_command() {
switch (context.command) {
WRITE_CONFIG:
return WRITE_CONFIG;
break;
READ_CONFIG:
return READ_CONFIG;
break;
READ_VOLTAGE:
return READ_VOLTAGE;
break;
READ_TERMPERATURE:
return READ_TERMPERATURE;
break;
READ_FAN_SPEED:
return READ_FAN_SPEED;
break;
CLEAR_BULK_FAN_SPEED:
return CLEAR_BULK_FAN_SPEED;
break;
UNKNOWN_COMMAND:
return 0xFF;
break;
}
}

View File

@ -12,6 +12,7 @@
extern "C" { extern "C" {
#endif #endif
#include <avr/io.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@ -59,10 +60,10 @@ typedef struct {
} command_context_t; } command_context_t;
// Parses the input string and outputs one of the valid commands // Parses the input string and outputs one of the valid commands
command_context_t parse_command(uint8_t *command, uint8_t command_len); void parse_command(uint8_t *command, uint8_t command_len);
// Routes the provided command to the appropriate function to handle it // Routes the provided command to the appropriate function to handle it
void route_command(command_context_t command); uint8_t route_command();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,18 +1,26 @@
#include "i2c.h" #include "i2c.h"
#include "command-handler.h"
// Basic I2C handling structure is heavily inspired by: https://github.com/microchip-pic-avr-examples/avr128db48-bare-metal-twi-mplab/blob/master/twi-client.X/peripherals/TWI/TWI_client.c // Basic I2C handling structure is heavily inspired by:
// https://github.com/microchip-pic-avr-examples/avr128db48-bare-metal-twi-mplab/blob/master/twi-client.X/peripherals/TWI/TWI_client.c
volatile uint8_t i2c_recv[I2C_RECV_BUF_SIZE]; // Arbitrary length array to hold the received data, longer than max expected command // We need to keep track of if the stop confition is in regards to a write or
volatile uint8_t i2c_recv_len; // read request
volatile bool last_action_write = false;
volatile uint8_t i2c_recv[I2C_RECV_BUF_SIZE] = {
0}; // Arbitrary length array to hold the received
// data, longer than max expected command
volatile uint8_t i2c_recv_len = 0;
void init_i2c(void) { void init_i2c(void) {
// Pin setup // Pin setup
PORTA.DIRSET = PIN2_bm | PIN3_bm; PORTA.DIRSET = PIN2_bm | PIN3_bm;
PORTA.PINCTRLUPD = PIN2_bm | PIN3_bm; PORTA.PINCTRLUPD = PIN2_bm | PIN3_bm;
// Enable operating in debug // Enable operating in debug
TWI0.DBGCTRL = TWI_DBGRUN_bm; TWI0.DBGCTRL = TWI_DBGRUN_bm;
// Initialize the control A register // Initialize the control A register
TWI0.CTRLA = TWI_INPUTLVL_I2C_gc // I2C voltage transition level TWI0.CTRLA = TWI_INPUTLVL_I2C_gc // I2C voltage transition level
| TWI_SDASETUP_4CYC_gc // Four clock cycles setup time | TWI_SDASETUP_4CYC_gc // Four clock cycles setup time
@ -30,7 +38,7 @@ void init_i2c(void) {
| PIN2_bm // Respond to all TWI addresses | PIN2_bm // Respond to all TWI addresses
| TWI_ENABLE_bm // Enable acting as a slave | TWI_ENABLE_bm // Enable acting as a slave
; ;
// Reset the received stuff // Reset the received stuff
i2c_reset_recv(); i2c_reset_recv();
} }
@ -39,59 +47,73 @@ void init_i2c(void) {
void i2c_reset_recv() { void i2c_reset_recv() {
i2c_recv_len = 0; i2c_recv_len = 0;
for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) { for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) {
i2c_recv[i] = 0; i2c_recv[i] = 0;
} }
} }
void i2c_write_handler(uint8_t data) { void i2c_write_handler(uint8_t data) {
i2c_recv[i2c_recv_len] = data; last_action_write = true;
i2c_recv_len++; i2c_recv[i2c_recv_len] = data;
i2c_recv_len++;
} }
void i2c_read_handler() { void i2c_read_handler() {
printf("Master wanted to read.\n"); last_action_write = false;
TWI0.SDATA = route_command();
} }
void i2c_stop_handler() { void i2c_stop_handler() {
// Reset counter and clear receive buffer if (last_action_write) {
i2c_recv[i2c_recv_len] = '\0'; // Parse the received command data
printf("%s\n", i2c_recv); parse_command(i2c_recv, i2c_recv_len);
i2c_reset_recv(); } else {
// route_command();
;
}
// Reset the buffer for future transmissions
i2c_reset_recv();
} }
// Address received // Address received
ISR(TWI0_TWIS_vect) { ISR(TWI0_TWIS_vect) {
// Check for the data interrupt flag // Disable interrupts while handling I2C
if (TWI0.SSTATUS & TWI_DIF_bm) { cli();
uint8_t data = 0;
// Check for the data interrupt flag
if ( ((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0 ) { if (TWI0.SSTATUS & TWI_DIF_bm) {
// Data write Master -> Slave uint8_t data = 0;
data = TWI0.SDATA;
if (((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0) {
// Send the data to the write handler // Data write Master -> Slave
i2c_write_handler(data); data = TWI0.SDATA;
} else {
// Data read Master <- Slave // Send the data to the write handler
i2c_read_handler(); i2c_write_handler(data);
data = TWI0.SDATA; } else {
} // Data read Master <- Slave
i2c_read_handler();
// Acknowledge having received // data = TWI0.SDATA;
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; // TWI0.SDATA = 1;
} }
// Check for address match or STOP // Acknowledge having received
if (TWI0.SSTATUS & TWI_APIF_bm) { TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
}
if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
// Address match, just send ack // Check for address match or STOP
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; if (TWI0.SSTATUS & TWI_APIF_bm) {
} else {
// STOP condition received if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
i2c_stop_handler(); // Address match, just send ack
// Send ACK TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; } else {
} // STOP condition received
i2c_stop_handler();
// Send ACK
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
} }
} }
// Re-enable interrupts
sei();
}

View File

@ -13,13 +13,15 @@ extern "C" {
#endif #endif
// Include the IO for I2C // Include the IO for I2C
#include <avr/io.h> #include "command-handler.h"
#include <avr/interrupt.h>
#include "uart.h" #include "uart.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdbool.h>
#include <util/twi.h> #include <util/twi.h>
// Received data info // Received data info
#define I2C_RECV_BUF_SIZE 64 #define I2C_RECV_BUF_SIZE 64
// Reset recv to initial state // Reset recv to initial state
void i2c_reset_recv(); void i2c_reset_recv();

View File

@ -11,17 +11,20 @@
#include <stdlib.h> #include <stdlib.h>
#include "uart.h" #include "uart.h"
#include "i2c.h" #include "i2c.h"
#include "command-handler.h"
#include <util/delay.h> #include <util/delay.h>
#include <avr/io.h>
int main() { int main() {
init_uart((uint16_t)9600); init_uart((uint16_t)9600);
init_i2c(); init_i2c();
stdout = &USART_stream; stdout = &USART_stream;
PORTB.DIRSET = PIN3_bm;
sei(); sei();
while (1) { while (1) {
printf("\n"); ;
_delay_ms(500);
} }
} }

View File

@ -149,6 +149,9 @@
<property key="program-the-device-with-default-config-words" value="false"/> <property key="program-the-device-with-default-config-words" value="false"/>
<property key="remove-unused-sections" value="true"/> <property key="remove-unused-sections" value="true"/>
</HI-TECH-LINK> </HI-TECH-LINK>
<Tool>
<property key="debugoptions.useswbreakpoints" value="true"/>
</Tool>
<XC8-CO> <XC8-CO>
<property key="coverage-enable" value=""/> <property key="coverage-enable" value=""/>
<property key="stack-guidance" value="false"/> <property key="stack-guidance" value="false"/>