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"
command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// Create the context
command_context_t context;
// Initialize empty, global command context
volatile command_context_t context = {UNKNOWN_COMMAND, SRC_NONE, FAN_NONE,
CNF_NONE};
void parse_command(uint8_t command[], uint8_t command_len) {
///////////////////////
// Command selection //
///////////////////////
@ -11,11 +12,12 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// Validate that we have a command
if (command_len < 1) {
context.command = UNKNOWN_COMMAND;
return context;
}
uint8_t foo = command[0];
// Figure out which command to run
switch (command[0]) {
switch (foo) {
case 0x11: // Read config
context.command = READ_CONFIG;
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.
if (context.command == READ_TERMPERATURE) {
return context;
return;
}
// Validate that we have a first parameter, else requirements for command are
// not fulfilled. return unknown command.
if (command_len < 2) {
context.command = UNKNOWN_COMMAND;
return context;
return;
}
// 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,
// return. Only config write requires a second parameter.
if (context.command != WRITE_CONFIG) {
return context;
return;
}
// Validate that we have a first parameter, else requirements for command are
// not fulfilled. return unknown command.
if (command_len < 3) {
context.command = UNKNOWN_COMMAND;
return context;
return;
}
// Store the parameter
@ -124,8 +126,32 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) {
// TODO: Handle the config parameters
// Return the context
return context;
// exit
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" {
#endif
#include <avr/io.h>
#include <stdint.h>
#include <string.h>
@ -59,10 +60,10 @@ typedef struct {
} command_context_t;
// 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
void route_command(command_context_t command);
uint8_t route_command();
#ifdef __cplusplus
}

View File

@ -1,18 +1,26 @@
#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
volatile uint8_t i2c_recv_len;
// We need to keep track of if the stop confition is in regards to a write or
// 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) {
// Pin setup
PORTA.DIRSET = PIN2_bm | PIN3_bm;
PORTA.PINCTRLUPD = PIN2_bm | PIN3_bm;
// Enable operating in debug
TWI0.DBGCTRL = TWI_DBGRUN_bm;
// Initialize the control A register
TWI0.CTRLA = TWI_INPUTLVL_I2C_gc // I2C voltage transition level
| TWI_SDASETUP_4CYC_gc // Four clock cycles setup time
@ -30,7 +38,7 @@ void init_i2c(void) {
| PIN2_bm // Respond to all TWI addresses
| TWI_ENABLE_bm // Enable acting as a slave
;
// Reset the received stuff
i2c_reset_recv();
}
@ -39,59 +47,73 @@ void init_i2c(void) {
void i2c_reset_recv() {
i2c_recv_len = 0;
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) {
i2c_recv[i2c_recv_len] = data;
i2c_recv_len++;
last_action_write = true;
i2c_recv[i2c_recv_len] = data;
i2c_recv_len++;
}
void i2c_read_handler() {
printf("Master wanted to read.\n");
last_action_write = false;
TWI0.SDATA = route_command();
}
void i2c_stop_handler() {
// Reset counter and clear receive buffer
i2c_recv[i2c_recv_len] = '\0';
printf("%s\n", i2c_recv);
i2c_reset_recv();
if (last_action_write) {
// Parse the received command data
parse_command(i2c_recv, i2c_recv_len);
} else {
// route_command();
;
}
// Reset the buffer for future transmissions
i2c_reset_recv();
}
// Address received
ISR(TWI0_TWIS_vect) {
// Check for the data interrupt flag
if (TWI0.SSTATUS & TWI_DIF_bm) {
uint8_t data = 0;
if ( ((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0 ) {
// Data write Master -> Slave
data = TWI0.SDATA;
// Send the data to the write handler
i2c_write_handler(data);
} else {
// Data read Master <- Slave
i2c_read_handler();
data = TWI0.SDATA;
}
// Acknowledge having received
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
// Disable interrupts while handling I2C
cli();
// Check for the data interrupt flag
if (TWI0.SSTATUS & TWI_DIF_bm) {
uint8_t data = 0;
if (((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0) {
// Data write Master -> Slave
data = TWI0.SDATA;
// Send the data to the write handler
i2c_write_handler(data);
} else {
// Data read Master <- Slave
i2c_read_handler();
// data = TWI0.SDATA;
// TWI0.SDATA = 1;
}
// Check for address match or STOP
if (TWI0.SSTATUS & TWI_APIF_bm) {
if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
// Address match, just send ack
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;
}
// Acknowledge having received
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
}
// Check for address match or STOP
if (TWI0.SSTATUS & TWI_APIF_bm) {
if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
// Address match, just send ack
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
// Include the IO for I2C
#include <avr/io.h>
#include <avr/interrupt.h>
#include "command-handler.h"
#include "uart.h"
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdbool.h>
#include <util/twi.h>
// Received data info
#define I2C_RECV_BUF_SIZE 64
#define I2C_RECV_BUF_SIZE 64
// Reset recv to initial state
void i2c_reset_recv();

View File

@ -11,17 +11,20 @@
#include <stdlib.h>
#include "uart.h"
#include "i2c.h"
#include "command-handler.h"
#include <util/delay.h>
#include <avr/io.h>
int main() {
init_uart((uint16_t)9600);
init_i2c();
stdout = &USART_stream;
PORTB.DIRSET = PIN3_bm;
sei();
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="remove-unused-sections" value="true"/>
</HI-TECH-LINK>
<Tool>
<property key="debugoptions.useswbreakpoints" value="true"/>
</Tool>
<XC8-CO>
<property key="coverage-enable" value=""/>
<property key="stack-guidance" value="false"/>