Merge branch 'command-handler'

This commit is contained in:
Sebastian H. Gabrielli 2024-04-16 17:02:59 +02:00
commit a9fec3f661
7 changed files with 411 additions and 18 deletions

View File

@ -0,0 +1,158 @@
#include "command-handler.h"
// 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 //
///////////////////////
// Validate that we have a command
if (command_len < 1) {
context.command = UNKNOWN_COMMAND;
}
// Extract the first byte, which contains the command
uint8_t command_byte = command[0];
// Figure out which command to run
switch (command_byte) {
case 0x11: // Read config
context.command = READ_CONFIG;
break;
case 0x12: // Read voltage
context.command = READ_VOLTAGE;
break;
case 0x13: // Read temperature
context.command = READ_TERMPERATURE;
break;
case 0x14: // Read current fan speed
context.command = READ_FAN_SPEED;
break;
case 0x15: // Read bulk fan speed
context.command = READ_BULK_FAN_SPEED;
break;
case 0x21: // Write config
context.command = WRITE_CONFIG;
break;
case 0x22: // Clear stored fan speed data
context.command = CLEAR_BULK_FAN_SPEED;
break;
default: // Unrecognized command
context.command = UNKNOWN_COMMAND;
break;
}
///////////////////////////////
// First parameter selection //
///////////////////////////////
// Check if the command does not require a parameter. If it does not, return.
if (context.command == READ_TERMPERATURE) {
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;
}
// Store the parameter
uint8_t param = command[1];
// Extract the parameter. Parameter is dependent on command
switch (command[0]) {
// Configuration parameters
case 0x11: // Read config
case 0x21: // Write config
// TODO: Handle parameters for config
break;
// Voltage parameters
case 0x12: // Read voltage
if (param == 0x01) {
context.src_voltage = SRC_INTERNAL;
} else if (param == 0x02) {
context.src_voltage = SRC_EXTRNAL;
} else if (param == 0x03) {
context.src_voltage = SRC_THERMISTOR;
} else {
context.src_voltage = SRC_NONE;
}
break;
// Fan parameters
case 0x14: // Read current fan speed
case 0x15: // Read bulk fan speed
case 0x22: // Clear stored fan speed data
context.command = READ_BULK_FAN_SPEED;
if (param == 0x01) {
context.fan = FAN1;
} else if (param == 0x02) {
context.fan = FAN2;
} else {
context.fan = FAN_NONE;
}
break;
// This should never be reached
default: // Unrecognized command
context.command = UNKNOWN_COMMAND;
break;
}
////////////////////////////////
// Second parameter selection //
////////////////////////////////
// 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;
}
// 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;
}
// Store the parameter
param = command[2];
// TODO: Handle the config parameters
// exit
return;
}
uint8_t route_command(int pos) {
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

@ -0,0 +1,74 @@
/*
* File: command-handler.h
* Author: Sebastian H. Gabrielli
*
* Created on March 6, 2024, 12:43 PM
*/
#ifndef COMMAND_HANDLER_H
#define COMMAND_HANDLER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <avr/io.h>
#include <stdint.h>
#include <string.h>
// Enum of all valid command types
typedef enum {
WRITE_CONFIG, // Change the configuration
READ_CONFIG, // Read, and print the current configuration
READ_VOLTAGE, // Read, and print a voltage
READ_TERMPERATURE, // Read, and print the temperature
READ_FAN_SPEED, // Read, and print the current fan speed
READ_BULK_FAN_SPEED, // Read, and print the stored back fan speed data
CLEAR_BULK_FAN_SPEED, // Clear the buffer of stored fan speed data
UNKNOWN_COMMAND // An unrecognized command has been sent
} command_t;
// Enum of all valid voltage sources
typedef enum {
SRC_INTERNAL, // Internal volage
SRC_EXTRNAL, // External voltage
SRC_THERMISTOR, // Thermistor voltage
SRC_NONE // No voltage source selected
} src_voltage_t;
// Enum of all valid config options
// TODO: Move into config header file
typedef enum {
CNF_NONE, // No config option
} config_option_t;
// Enum of all valid fans
// TODO: Consider moving into it's own fan page
typedef enum {
FAN1, // The first fan
FAN2, // The second fan
FAN_NONE // No fan
} fans_t;
// Struct with command context
typedef struct {
command_t command; // The command to execute
src_voltage_t src_voltage; // The selected voltage source
fans_t fan; // The selected fan
config_option_t conf; // The configuration option to cange
// TODO: Add config value field for writing
} command_context_t;
// Parses the input string and outputs one of the valid commands
void parse_command(uint8_t *command, uint8_t command_len);
// Routes the provided command to the appropriate function to handle it
// If the command is a read command it then returns the current byte.
// The position is the byte to read when multiple bytes are being read.
uint8_t route_command(int pos);
#ifdef __cplusplus
}
#endif
#endif /* COMMAND_HANDLER_H */

117
prosjekt.X/i2c.c Normal file
View File

@ -0,0 +1,117 @@
#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
// 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
| TWI_SDAHOLD_50NS_gc // 50ns SDA hold time
| TWI_FMPEN_OFF_gc // Standard SPI timing
;
// The device's slave address
TWI0.SADDR = 0x42;
// Enable acting as a slave
TWI0.SCTRLA = TWI_DIEN_bm // Enable data interrupt
| TWI_APIEN_bm // Enable more interrupts
| TWI_PIEN_bm // Enable stop flag interrupt
| PIN2_bm // Respond to all TWI addresses
| TWI_ENABLE_bm // Enable acting as a slave
;
// Reset the received stuff
i2c_reset_recv();
}
// Reset received counter and clear receive buffer
void i2c_reset_recv() {
i2c_recv_len = 0;
for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) {
i2c_recv[i] = 0;
}
}
void i2c_write_handler(uint8_t data) {
last_action_write = true;
i2c_recv[i2c_recv_len] = data;
i2c_recv_len++;
}
void i2c_read_handler() {
last_action_write = false;
TWI0.SDATA = route_command(i2c_recv_len);
i2c_recv_len++; // Increment the counter of the current amount of requests
}
void i2c_stop_handler() {
if (last_action_write) {
// Parse the received command data
parse_command(i2c_recv, i2c_recv_len);
}
// Reset the buffer for future transmissions
i2c_reset_recv();
}
// Address received
ISR(TWI0_TWIS_vect) {
// 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;
}
// 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();
}

36
prosjekt.X/i2c.h Normal file
View File

@ -0,0 +1,36 @@
/*
* File: i2c.h
* Author: sebgab
*
* Created on March 6, 2024, 1:53 PM
*/
#ifndef I2C_H
#define I2C_H
#ifdef __cplusplus
extern "C" {
#endif
// Include the IO for I2C
#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
// Reset recv to initial state
void i2c_reset_recv();
// Initialize the I2C bus
void init_i2c(void);
#ifdef __cplusplus
}
#endif
#endif /* I2C_H */

View File

@ -1,34 +1,38 @@
/*
/*
* File: main.c
* Author: Sebastian H. Gabrielli, Helle Augland Grasmo
*
* Created on March 6, 2024, 12:34 PM
*/
#include "voltage.h"
#include "uart.h"
#include "voltage.h"
#define RTC_PERIOD (511)
#define DELAY_TIME 1000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <stdbool.h>
#include "eeprom.h"
#include <avr/interrupt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define F_CPU 4E6
#include "command-handler.h"
#include "i2c.h"
#include "uart.h"
#include <avr/io.h>
#include <util/delay.h>
int main() {
ADC0_init();
init_uart((uint16_t)9600);
stdout = &USART_stream;
init_uart((uint16_t)9600);
init_i2c();
stdout = &USART_stream;
while (1) {
uint16_t adcVal = ADC0_read();
printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal);
}
}
PORTB.DIRSET = PIN3_bm;
sei();
while (1) {
// uint16_t adcVal = ADC0_read();
// printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal);
;
}
}

View File

@ -4,6 +4,8 @@
<logicalFolder name="HeaderFiles"
displayName="Header Files"
projectFiles="true">
<itemPath>command-handler.h</itemPath>
<itemPath>i2c.h</itemPath>
<itemPath>uart.h</itemPath>
<itemPath>eeprom.h</itemPath>
<itemPath>voltage.h</itemPath>
@ -16,6 +18,8 @@
displayName="Source Files"
projectFiles="true">
<itemPath>main.c</itemPath>
<itemPath>command-handler.c</itemPath>
<itemPath>i2c.c</itemPath>
<itemPath>uart.c</itemPath>
<itemPath>eeprom.c</itemPath>
<itemPath>voltage.c</itemPath>

View File

@ -20,7 +20,7 @@ extern "C" {
#endif
#define USART3_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)
// Initialize the USART3 controller
void init_uart(uint16_t baud);