diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c new file mode 100644 index 0000000..e9ade45 --- /dev/null +++ b/prosjekt.X/command-handler.c @@ -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; + } +} diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h new file mode 100644 index 0000000..e09afda --- /dev/null +++ b/prosjekt.X/command-handler.h @@ -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 +#include +#include + +// 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 */ diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c new file mode 100644 index 0000000..c11fdcf --- /dev/null +++ b/prosjekt.X/i2c.c @@ -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(); +} diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h new file mode 100644 index 0000000..342f459 --- /dev/null +++ b/prosjekt.X/i2c.h @@ -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 +#include +#include +#include + +// 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 */ diff --git a/prosjekt.X/main.c b/prosjekt.X/main.c index 364a28b..80e332b 100644 --- a/prosjekt.X/main.c +++ b/prosjekt.X/main.c @@ -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 -#include -#include -#include -#include - - #include "eeprom.h" +#include +#include #include #include +#include #define F_CPU 4E6 +#include "command-handler.h" +#include "i2c.h" #include "uart.h" +#include #include 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); - } -} \ No newline at end of file + PORTB.DIRSET = PIN3_bm; + + sei(); + + while (1) { + // uint16_t adcVal = ADC0_read(); + // printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal); + ; + } +} diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index 3b13f57..d9ab8f2 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -4,6 +4,8 @@ + command-handler.h + i2c.h uart.h eeprom.h voltage.h @@ -16,6 +18,8 @@ displayName="Source Files" projectFiles="true"> main.c + command-handler.c + i2c.c uart.c eeprom.c voltage.c diff --git a/prosjekt.X/uart.h b/prosjekt.X/uart.h index 7284552..9ca2621 100644 --- a/prosjekt.X/uart.h +++ b/prosjekt.X/uart.h @@ -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);