diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c index e9ade45..347043c 100644 --- a/prosjekt.X/command-handler.c +++ b/prosjekt.X/command-handler.c @@ -54,7 +54,7 @@ void parse_command(uint8_t command[], uint8_t command_len) { return; } - // Validate that we have a first parameter, else requirements for command are + // Validate that we have a second parameter, else requirements for command are // not fulfilled. return unknown command. if (command_len < 2) { context.command = UNKNOWN_COMMAND; @@ -69,7 +69,11 @@ void parse_command(uint8_t command[], uint8_t command_len) { // Configuration parameters case 0x11: // Read config case 0x21: // Write config - // TODO: Handle parameters for config + if (param == 0x01) { + context.conf = SAMPLE_TIME; + } else { + context.conf = CNF_NONE; + } break; // Voltage parameters @@ -89,7 +93,6 @@ void parse_command(uint8_t command[], uint8_t command_len) { 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) { @@ -105,9 +108,9 @@ void parse_command(uint8_t command[], uint8_t command_len) { break; } - //////////////////////////////// - // Second parameter selection // - //////////////////////////////// + ///////////////////////////////// + // Third parameter selection // + ///////////////////////////////// // Check if the command does not require a second parameter. If it does not, // return. Only config write requires a second parameter. @@ -115,7 +118,7 @@ void parse_command(uint8_t command[], uint8_t command_len) { return; } - // Validate that we have a first parameter, else requirements for command are + // Validate that we have a third parameter, else requirements for command are // not fulfilled. return unknown command. if (command_len < 3) { context.command = UNKNOWN_COMMAND; @@ -125,34 +128,139 @@ void parse_command(uint8_t command[], uint8_t command_len) { // Store the parameter param = command[2]; - // TODO: Handle the config parameters + context.conf = param; + + //////////////////////////////// + // Fourth parameter selection // + //////////////////////////////// + + // Validate that we have a fourth parameter, else requirements for command are + // not fulfilled. return unknown command. Second parameter is u16, thus two bytes. + if (command_len < 4) { + context.command = UNKNOWN_COMMAND; + return; + } + + // Extract the value + union { + uint16_t value; + uint8_t bytes[2]; + } config_value; + + config_value.bytes[0] = command[3]; + config_value.bytes[1] = command[4]; + + // Store the value + context.conf_val = config_value.value; + // 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; + switch (context.command) { + case WRITE_CONFIG: + switch (context.conf) { + case SAMPLE_TIME: + // Overwrite the config value + config.ms_fanspeed_sample_rate = context.conf_val; + + // Set the flag to store it in the EEPROM + store_config = true; + break; + } + break; + case READ_CONFIG: + { + switch (context.conf) { + case SAMPLE_TIME: + { + // Validate that pos is within the valid range + if (pos >= 2) { return 0x00; } + + // Config only has one parameter so we sent that parameter + // Create a union to store the data + union { + uint16_t value; + uint8_t bytes[2]; + } config_value; + config_value.value = config.ms_fanspeed_sample_rate; + + // Return the corresponding data byte + return config_value.bytes[1-pos]; + } + break; + } + } break; + + case READ_VOLTAGE: + { + // Create a union to store the data + union { + int16_t v; + uint8_t bytes[2]; + } voltage; + + // Figure out which voltage source to read + switch (context.src_voltage) { + case SRC_INTERNAL: + voltage.v = internal_voltage_read(); + break; + case SRC_EXTRNAL: + voltage.v = external_voltage_read(); + break; + case SRC_THERMISTOR: + voltage.v = thermistor_voltage_read(); + break; + default: + return 0xFF; + break; + } + + // Send the data + return voltage.bytes[pos]; + } + case READ_TERMPERATURE: + { + uint16_t v_therm = thermistor_voltage_read(); + union { + int16_t temp; + uint8_t bytes[2]; + } temperature; + temperature.temp = (int16_t) ( calculate_thermistor_temp(v_therm) * 1000 ); + return temperature.bytes[pos]; + } + break; + case READ_FAN_SPEED: + if (context.fan == FAN1) { + return fan1_history[0]; + } else if (context.fan == FAN2) { + return fan2_history[0]; + } else { + return 0; + } + break; + case READ_BULK_FAN_SPEED: + if (context.fan == FAN1) { + return fan1_history[pos]; + } else if (context.fan == FAN2) { + return fan2_history[pos]; + } else { + return 0; + } + break; + case CLEAR_BULK_FAN_SPEED: + // Overwrite the content of the desired fan array with zeroes + if (context.fan == FAN1) { + memset(fan1_history, 0, sizeof(fan1_history)); + } else if (context.fan == FAN2) { + memset(fan2_history, 0, sizeof(fan2_history)); + } + break; + case UNKNOWN_COMMAND: + default: + return 0xFF; } } diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h index e09afda..cf0f428 100644 --- a/prosjekt.X/command-handler.h +++ b/prosjekt.X/command-handler.h @@ -12,20 +12,23 @@ extern "C" { #endif +#include "eeprom.h" #include #include #include - +#include "voltage.h" +#include "themistor-temp.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 + WRITE_CONFIG = 0x21, // Change the configuration + READ_CONFIG = 0x11, // Read, and print the current configuration + READ_VOLTAGE = 0x12, // Read, and print a voltage + READ_TERMPERATURE = 0x13, // Read, and print the temperature + READ_FAN_SPEED = 0x14, // Read, and print the current fan speed + READ_BULK_FAN_SPEED = 0x15, // Read, and print the stored back fan speed data + CLEAR_BULK_FAN_SPEED = 0x22, // Clear the buffer of stored fan speed data + UNKNOWN_COMMAND // An unrecognized command has been sent } command_t; // Enum of all valid voltage sources @@ -39,14 +42,15 @@ typedef enum { // Enum of all valid config options // TODO: Move into config header file typedef enum { - CNF_NONE, // No config option + SAMPLE_TIME = 0x01, // Time between each fan speed sample + 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 + FAN1 = 1, // The first fan + FAN2 = 2, // The second fan FAN_NONE // No fan } fans_t; @@ -56,9 +60,17 @@ typedef struct { 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 + uint16_t conf_val; // The value of the config option to change } command_context_t; +// Fan history variables +extern volatile uint16_t fan1_history[512]; +extern volatile uint16_t fan2_history[512]; + +// Config +extern volatile config_t config; +extern volatile bool store_config; + // Parses the input string and outputs one of the valid commands void parse_command(uint8_t *command, uint8_t command_len); diff --git a/prosjekt.X/eeprom.c b/prosjekt.X/eeprom.c index 5dd4616..a0db672 100644 --- a/prosjekt.X/eeprom.c +++ b/prosjekt.X/eeprom.c @@ -1,17 +1,9 @@ #include "eeprom.h" // The start address for the controller data -uint8_t EEMEM start_address_controller = 0x00; +uint16_t EEMEM start_address_controller = 0x1400; -// Where the writing of the fans points start -uint8_t EEMEM start_address_fan1 = 0x30; -uint8_t EEMEM start_address_fan2 = 0x60; - -// The placement for the next datapoint form the fans. -uint8_t EEMEM current_address_fan1 = 0x30; -uint8_t EEMEM current_address_fan2 = 0x60; - -// Checks if the EEPROM memory is ready to be written in. +// Checks if the EEPROM memory is ready to be written in and waits until it is. void check_eeprom_is_ready(){ while(1){ if (eeprom_is_ready()){ @@ -23,77 +15,28 @@ void check_eeprom_is_ready(){ } -// Takes inn a struct by the form of config_t -// Checks if the eeprom is ready to be written in -// Checks if it has been written information at the address -// If true, the infromation is replaced with the intaken struct -// else the intaken struct is written at the address. void write_struct_from_EEPROM(config_t write_struct){ + // Calculate the required storage size uint8_t struct_size = sizeof(write_struct); + // Wait for the EEPROM to be ready check_eeprom_is_ready(); + // Update the stored config stuct eeprom_update_block((void*) &write_struct,(void*) &start_address_controller, struct_size); - } -// Reads the memory block at the address start_address_controller -// returns a struct in form of config_t config_t read_struct_from_EEPROM(){ - //is eeprom ready?? - config_t read_struct; + // Create a config struct to hold the received data + config_t read_struct = { 0 }; uint8_t struct_size = sizeof(read_struct); + // Wait for the EEPROM to be ready check_eeprom_is_ready(); + // Read the data from the EEPROM eeprom_read_block((void *) &read_struct,(void*) &start_address_controller, struct_size); + + // Return the data return read_struct; -} - -// Takes inn a dataPoint and what data set it belongs to -// checks if EEPROM is ready -// If the dataset is 1, the datapoint is written at the address. -// If the dataset is 2 its written at another point. -int write_data_point_in_EEPROM(uint8_t byte, uint8_t fan_num){ - - check_eeprom_is_ready(); - if (fan_num == 1){ - eeprom_update_byte(current_address_fan1, byte); - current_address_fan1++; - return 1; - } else if (fan_num == 2){ - eeprom_update_byte(current_address_fan2, byte); - current_address_fan2++; - return 1; - } else{ - return 0; - } -} - -// Reads all the datapoints to the choosen data. -// it writes the data points in the USART stream. -uint8_t read_data_point_speed_info(uint8_t fan_num, uint8_t *array){ - uint8_t byte = 0; - - if (fan_num == 1){ - uint8_t len = current_address_fan1 - start_address_fan1; - - check_eeprom_is_ready(); - - for (uint8_t i = 0; i #include - - // Struct for information on the controller. typedef struct { - uint8_t fanSpeed; + uint16_t ms_fanspeed_sample_rate; } config_t; -// Check if EEPROM is ready to be written in +// Check if EEPROM is ready to be written to void check_eeprom_is_ready(); -// Writes a struct in EEPROM +// Writes a config_t struct to the EEPROM void write_struct_from_EEPROM(config_t write_struct); -// Read data from EEPROM and return it as a controller struct +// Read data from EEPROM and return it as a config_t struct config_t read_struct_from_EEPROM(); -// Writes a datapoint in EEPROM -int write_data_point_from_EEPROM(uint8_t byte, uint8_t fan_num); - -// Reads all the dataPoints form EEPROM -uint8_t read_data_point_speed_info(uint8_t fan_num, uint8_t *array); - #ifdef __cplusplus } diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c index c11fdcf..ded4482 100644 --- a/prosjekt.X/i2c.c +++ b/prosjekt.X/i2c.c @@ -8,9 +8,9 @@ // 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 +// Buffer to hold the received data +volatile uint8_t i2c_recv_buf[I2C_RECV_BUF_SIZE] = {0}; +// Counter to know which datapoint we're on volatile uint8_t i2c_recv_len = 0; void init_i2c(void) { @@ -47,13 +47,18 @@ 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_buf[i] = 0; } } void i2c_write_handler(uint8_t data) { last_action_write = true; - i2c_recv[i2c_recv_len] = data; + + // Validate that we are not overflowing the buffer + if (i2c_recv_len >= I2C_RECV_BUF_SIZE) { return; } + + // Write the data to the receive buffer + i2c_recv_buf[i2c_recv_len] = data; i2c_recv_len++; } @@ -66,8 +71,14 @@ void i2c_read_handler() { void i2c_stop_handler() { if (last_action_write) { // Parse the received command data - parse_command(i2c_recv, i2c_recv_len); + parse_command(i2c_recv_buf, i2c_recv_len); + + // If the received command is a write only command we want to route it now. + if (i2c_recv_buf[0] == CLEAR_BULK_FAN_SPEED || i2c_recv_buf[0] == WRITE_CONFIG) { + route_command(0); + } } + // Reset the buffer for future transmissions i2c_reset_recv(); } @@ -79,19 +90,16 @@ 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; + // Data write Controller -> Target + uint8_t data = TWI0.SDATA; // Send the data to the write handler i2c_write_handler(data); } else { - // Data read Master <- Slave + // Data read Controller <- Target i2c_read_handler(); - // data = TWI0.SDATA; - // TWI0.SDATA = 1; } // Acknowledge having received diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h index 342f459..1272b11 100644 --- a/prosjekt.X/i2c.h +++ b/prosjekt.X/i2c.h @@ -20,8 +20,9 @@ extern "C" { #include #include -// Received data info -#define I2C_RECV_BUF_SIZE 64 +// Received data buffer size +// The size is larger than any expected command length +#define I2C_RECV_BUF_SIZE 16 // Reset recv to initial state void i2c_reset_recv(); diff --git a/prosjekt.X/main.c b/prosjekt.X/main.c index 428b4ff..2fe917d 100644 --- a/prosjekt.X/main.c +++ b/prosjekt.X/main.c @@ -1,40 +1,56 @@ /* * File: main.c - * Author: Sebastian H. Gabrielli, Helle Augland Grasmo + * Author: Sebastian H. Gabrielli, Helle Augland Grasmo, Ina Min Rørnes * * Created on March 6, 2024, 12:34 PM */ +#include #include "uart.h" #include "voltage.h" #define RTC_PERIOD (511) #define DELAY_TIME 1000 #include "eeprom.h" #include -#include #include #include #include #define F_CPU 4E6 #include "command-handler.h" #include "i2c.h" +#include "themistor-temp.h" #include "uart.h" #include -#include #include -#include "themistor-temp.h" +#include + +// Fan history variables +volatile uint16_t fan1_history[512] = { 0 }; +volatile uint16_t fan2_history[512] = { 0 }; + +// Default config is 500ms sample rate +volatile config_t config = { 500 }; +volatile bool store_config = false; int main() { + // Initialize functionality init_uart((uint16_t)9600); + ADC0_init(); + init_led(); init_i2c(); stdout = &USART_stream; + // Read the stored config struct + config = read_struct_from_EEPROM(); + PORTB.DIRSET = PIN3_bm; - + sei(); while (1) { - // uint16_t adcVal = ADC0_read(); - // printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal); - ; + // If we have made a config change, store it. + if (store_config) { + write_struct_from_EEPROM(config); + store_config = false; + } } } diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index 9e4c7b2..af7016b 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -27,6 +27,9 @@ uart.c eeprom.c voltage.c + i2c.c + command-handler.c + thermistor-temp.c Makefile @@ -39,7 +42,7 @@ nEdbgTool XC8 - 2.45 + 2.46 2