Compare commits

..

No commits in common. "main" and "fanspeed-comparator" have entirely different histories.

16 changed files with 126 additions and 941 deletions

View File

@ -1,307 +0,0 @@
#include "command-handler.h"
#include "fan-speed.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 second 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
if (param == 0x01) {
context.conf = SAMPLE_TIME;
} else {
context.conf = CNF_NONE;
}
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
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;
}
/////////////////////////////////
// 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.
if (context.command != WRITE_CONFIG) {
return;
}
// 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;
return;
}
// Store the parameter
param = command[1];
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[2];
config_value.bytes[1] = command[3];
// Store the value
context.conf_val = config_value.value;
// exit
return;
}
uint8_t route_command(int pos) {
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;
return 0;
break;
case CNF_NONE:
return 0;
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:
{
// Validate that pos is within range
if (pos >= 2) { return 0; }
// 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[1-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:
// Validate that pos is within range
if (pos >= 2) { return 0; }
// Union to hold the u16
if (context.fan == FAN1) {
union {
uint16_t val;
uint8_t bytes[2];
} speed;
speed.val = fan1_history[fan1_history_index];
return speed.bytes[1-pos];
} else if (context.fan == FAN2) {
union {
uint16_t val;
uint8_t bytes[2];
} speed;
speed.val = fan2_history[fan2_history_index];
return speed.bytes[1-pos];
} else {
return 0;
}
break;
case READ_BULK_FAN_SPEED:
if (context.fan == FAN1) {
// Validate that pos is valid, if not return 0
if (pos >= 512) { return 0; }
// Calculate the index position
int16_t index = fan1_history_index - pos;
if (index < 0) { index = 511-pos; }
return fan1_history[index];
} else if (context.fan == FAN2) {
// Validate that pos is valid, if not return 0
if (pos >= 512) { return 0; }
// Calculate the index position
int16_t index = fan2_history_index - pos;
if (index < 0) { index = 511-pos; }
return fan2_history[index];
} 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));
}
return 0;
break;
case UNKNOWN_COMMAND:
default:
return 0xFF;
}
}

View File

@ -1,89 +0,0 @@
/*
* 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 "eeprom.h"
#include <avr/io.h>
#include <stdint.h>
#include <string.h>
#include "voltage.h"
#include "themistor-temp.h"
// Enum of all valid command types
typedef enum {
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
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 {
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 = 1, // The first fan
FAN2 = 2, // 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
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];
// Fan history index variable
extern volatile uint16_t fan1_history_index;
extern volatile uint16_t fan2_history_index;
// 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);
// 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 */

View File

@ -1,42 +0,0 @@
#include "eeprom.h"
// The start address for the controller data
uint16_t EEMEM start_address_controller = 0x1400;
// 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()){
break;
}else{
;
}
}
}
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);
}
config_t read_struct_from_EEPROM(){
// 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;
}

View File

@ -1,46 +0,0 @@
/*
* File: eeprom.h
* Author: Helle Augland Grasmo, Sebastian H. Gabrielli
*
* Created on 06 March 2024, 15:30
*/
#ifndef EEPROM_H
#define EEPROM_H
#ifdef __cplusplus
extern "C" {
#endif
#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/eeprom.h>
#include <stdbool.h>
// The code is inspired by "EEPROM handling" form avr-libc
// web link: https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html
// Struct for information on the controller.
typedef struct {
uint16_t ms_fanspeed_sample_rate;
} config_t;
// Check if EEPROM is ready to be written to
void check_eeprom_is_ready();
// 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 config_t struct
config_t read_struct_from_EEPROM();
#ifdef __cplusplus
}
#endif
#endif /* EEPROM_H */

View File

@ -1,7 +1,7 @@
#include "fan-speed.h" #include "fan_speeeed.h"
#include "uart.h" #include "uart.h"
uint16_t timer_period_ms = 1000; uint16_t timer_period_ms = 1;
uint16_t fan_speed = 0; uint16_t fan_speed = 0;
volatile uint16_t fan1_edge_counter = 0; volatile uint16_t fan1_edge_counter = 0;
volatile uint16_t fan2_edge_counter = 0; volatile uint16_t fan2_edge_counter = 0;
@ -10,21 +10,18 @@ volatile uint16_t fan2_edge_counter = 0;
void init_TCA0() { void init_TCA0() {
TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm ; TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm ;
TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm | TCA_SINGLE_CLKSEL_DIV1024_gc ; /* Sysclk /1024 */ TCA0.SINGLE.CTRLA = TCA_SINGLE_ENABLE_bm | TCA_SINGLE_CLKSEL_DIV1024_gc ; /* Sysclk /1024 */
TCA0_update_period(timer_period_ms);
} }
void TCA0_update_period(uint16_t period_ms) { void TCA0_update_period(uint16_t timer_period) {
float period_s = period_ms / 1E3; // Convert the ms to s TCA0.SINGLE.PERBUF = (F_CPU * (1 / timer_period) / 1024); /* F_CPU * F_IRQ / TCA_prescaler */
float frequency = 1/ period_s; // convert the period to a frequency
float perbuf_val = frequency * F_CPU / 1024; // F_IQR * F_CPU / TCA_prescaler
TCA0.SINGLE.PERBUF = (uint16_t)perbuf_val;
timer_period_ms = period_ms;
} }
// COUNTINGS / TIME = FREQUENCY // COUNTINGS / TIME = FREQUENCY
// FREQ / 2 = GIVES ACTUAL FREQ // FREQ / 2 = GIVES ACTUAL FREQ
// FREQ * SEC-IN-A-MINUTE(60) / FAN-BLADES // FREQ * SEC-IN-A-MINUTE(60) / FAN-BLADES
uint16_t RPM_calculation(uint16_t edge_counter, uint16_t time_ms) { uint16_t RPM_calculation(uint16_t edge_counter, uint16_t time_ms) {
fan_speed = (edge_counter / (float)((float)time_ms/1000.0) ); fan_speed = (edge_counter / time_ms);
fan_speed = fan_speed/2; fan_speed = fan_speed/2;
fan_speed = fan_speed * 60/5; fan_speed = fan_speed * 60/5;
edge_counter = 0; edge_counter = 0;
@ -101,25 +98,8 @@ ISR(AC1_AC_vect){ // AC1 vec flag
// TIMER INTERUPT // TIMER INTERUPT
ISR (TCA0_OVF_vect) { ISR (TCA0_OVF_vect) {
cli(); cli();
RPM_calculation(fan1_edge_counter,timer_period_ms);
// Increment the index, or reset if it is at the top RPM_calculation(fan2_edge_counter,timer_period_ms);
if (fan1_history_index < 512) {
fan1_history_index++;
} else {
fan1_history_index = 0;
}
if (fan2_history_index < 512) {
fan2_history_index++;
} else {
fan2_history_index = 0;
}
// Calculate the fanspeed
fan1_history[fan1_history_index] = RPM_calculation(fan1_edge_counter, timer_period_ms);
fan2_history[fan2_history_index] = RPM_calculation(fan2_edge_counter, timer_period_ms);
// Reset the edge counter
fan1_edge_counter = 0; fan1_edge_counter = 0;
fan2_edge_counter = 0; fan2_edge_counter = 0;
TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm ; TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm ;

View File

@ -15,47 +15,40 @@ extern "C" {
#endif #endif
#include <stdbool.h> #include <stdbool.h>
#include <float.h> #include <float.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <math.h> #include <math.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <avr/io.h> #include <avr/io.h>
/* /*
The code has inspiration from "Getting Started with Analog comparator(AC)" by Microchip for setting up analog comparrator. The code has inspiration from "Getting Started with Analog comparator(AC)" by Microchip for setting up analog comparrator.
* web link: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ApplicationNotes/ApplicationNotes/TB3211-Getting-Started-with-AC-DS90003211.pdf * web link: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ApplicationNotes/ApplicationNotes/TB3211-Getting-Started-with-AC-DS90003211.pdf
* and inspiration form practice 6 for TCA0 setup * and inspiration form practice 6 for TCA0 setup
*/ */
// Fan history variables
extern volatile uint16_t fan1_history[512];
extern volatile uint16_t fan2_history[512];
// Fan history index variable
extern volatile uint16_t fan1_history_index;
extern volatile uint16_t fan2_history_index;
// INITALICE TIMER COUNTER // INITALICE TIMER COUNTER
void init_TCA0(); void init_TCA0();
// UPDATE TIMER PERIOD // UPDATE TIMER PERIOD
void TCA0_update_period_ms (uint16_t timer_period); void TCA0_update_period_ms (uint16_t timer_period);
// TAKES INN A TIME AND A THE COUNTED FAN DIPS // TAKES INN A TIME AND A THE COUNTED FAN DIPS
// RETURNS THE RPM OF THE FAN // RETURNS THE RPM OF THE FAN
uint16_t RPM_calculation(uint16_t edge_counter, uint16_t time_ms); uint16_t RPM_calculation(uint16_t edge_counter, uint16_t time_ms);
// INITIALISING FAN PORTS // INITIALISING FAN PORTS
void init_fan_gpio(); void init_fan_gpio();
// INIT AC0 TO COMPARE PD6 AND PD7 // INIT AC0 TO COMPARE PD6 AND PD7
void init_AC0(); void init_AC0();
// INIT AC1 TO COMPARE PD4 AND PD7 // INIT AC1 TO COMPARE PD4 AND PD7
void init_AC1(); void init_AC1();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,125 +0,0 @@
#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;
// 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) {
// 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_buf[i] = 0;
}
}
void i2c_write_handler(uint8_t data) {
last_action_write = true;
// 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++;
}
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_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();
}
// Address received
ISR(TWI0_TWIS_vect) {
// Disable interrupts while handling I2C
cli();
// Check for the data interrupt flag
if (TWI0.SSTATUS & TWI_DIF_bm) {
if (((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0) {
// Data write Controller -> Target
uint8_t data = TWI0.SDATA;
// Send the data to the write handler
i2c_write_handler(data);
} else {
// Data read Controller <- Target
i2c_read_handler();
}
// 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

@ -1,36 +0,0 @@
/*
* 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 <avr/interrupt.h>
#include <avr/io.h>
#include <stdbool.h>
#include <util/twi.h>
// 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();
// Initialize the I2C bus
void init_i2c(void);
#ifdef __cplusplus
}
#endif
#endif /* I2C_H */

View File

@ -1,83 +1,43 @@
/* /*
* File: main.c * File: main.c
* Author: Sebastian H. Gabrielli, Helle Augland Grasmo, Ina Min Rørnes * Author: Sebastian H. Gabrielli
* *
* Created on March 6, 2024, 12:34 PM * Created on March 6, 2024, 12:34 PM
*/ */
#define F_CPU 4E6 #include "voltage.h"
#include "uart.h"
// Include AVR specific libs #define RTC_PERIOD (511)
#include <avr/interrupt.h> #define DELAY_TIME 1000
#include <avr/io.h> #include <avr/io.h>
#include <util/delay.h> #include <avr/interrupt.h>
#include <util/delay.h>
// Include standard C libraries #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> //#define F_CPU 4E6
#include <stdlib.h> #include <stdio.h>
#include <string.h> #include <stdlib.h>
#include <stdint.h> #include "uart.h"
#include <util/delay.h>
// Include custom source files #include <stdint.h>
#include "eeprom.h" #include "themistor-temp.h"
#include "voltage.h" #include "fan_speeeed.h"
#include "command-handler.h"
#include "i2c.h" #define F_CPU 4000000UL
#include "themistor-temp.h"
#include "fan-speed.h"
// Only enable UART when required for debugging
#ifdef ENABLE_UART int main(void) {
#include "uart.h"
#endif init_uart((uint16_t)9600);
stdout = &USART_stream;
// Fan history variables init_TCA0();
volatile uint16_t fan1_history[512] = {0}; init_fan_gpio();
volatile uint16_t fan2_history[512] = {0}; init_AC0();
// Fan history index variable init_AC1();
volatile uint16_t fan1_history_index = 0; sei();
volatile uint16_t fan2_history_index = 0;
while (1) {
// Default config is 500ms sample rate ;
volatile config_t config = {500}; }
volatile bool store_config = false; }
int main() {
// Initialize functionality
ADC0_init();
init_alarm_gpio();
init_i2c();
// Fanspeed
init_AC0();
init_AC1();
init_TCA0();
TCA0_update_period(config.ms_fanspeed_sample_rate);
// Only enable UART when required for debugging
#ifdef ENABLE_UART
init_uart((uint16_t)9600);
stdout = &USART_stream;
#endif
// Read the stored config struct
config = read_struct_from_EEPROM();
// Enable interrupts
sei();
while (1) {
// If we have made a config change, store it and recalculate the period.
if (store_config) {
TCA0_update_period(config.ms_fanspeed_sample_rate);
write_struct_from_EEPROM(config);
store_config = false;
}
}
// Check the temperature, and start the alarm if required
uint16_t thermistor_voltage = thermistor_voltage_read();
float temperature = calculate_thermistor_temp(thermistor_voltage);
bool start_alarm = voltage_threshold_bool(temperature, 40);
alert_voltage_threshold_exceeded(start_alarm);
}

View File

@ -6,11 +6,8 @@
projectFiles="true"> projectFiles="true">
<itemPath>uart.h</itemPath> <itemPath>uart.h</itemPath>
<itemPath>voltage.h</itemPath> <itemPath>voltage.h</itemPath>
<itemPath>fan_speeeed.h</itemPath>
<itemPath>themistor-temp.h</itemPath> <itemPath>themistor-temp.h</itemPath>
<itemPath>command-handler.h</itemPath>
<itemPath>eeprom.h</itemPath>
<itemPath>i2c.h</itemPath>
<itemPath>fan-speed.h</itemPath>
</logicalFolder> </logicalFolder>
<logicalFolder name="ExternalFiles" <logicalFolder name="ExternalFiles"
displayName="Important Files" displayName="Important Files"
@ -28,11 +25,8 @@
<itemPath>main.c</itemPath> <itemPath>main.c</itemPath>
<itemPath>uart.c</itemPath> <itemPath>uart.c</itemPath>
<itemPath>voltage.c</itemPath> <itemPath>voltage.c</itemPath>
<itemPath>fan_speeeed.c</itemPath>
<itemPath>thermistor-temp.c</itemPath> <itemPath>thermistor-temp.c</itemPath>
<itemPath>command-handler.c</itemPath>
<itemPath>i2c.c</itemPath>
<itemPath>eeprom.c</itemPath>
<itemPath>fan-speed.c</itemPath>
</logicalFolder> </logicalFolder>
</logicalFolder> </logicalFolder>
<projectmakefile>Makefile</projectmakefile> <projectmakefile>Makefile</projectmakefile>

View File

@ -13,34 +13,24 @@ extern "C" {
#endif #endif
#include <stdbool.h> #include <stdbool.h>
#include <float.h> #include <float.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <math.h> #include <math.h>
#include <avr/io.h> #define R_t0 10000
#define R_T0 100E3
#define T_0 298.15 #define T_0 298.15
#define B 3950 #define B 3950
#define R_1 1000 #define R_1 1000
#define ledpin PIN3_bm
// Takes inn messured value
// Takes inn messured value
#define ALERT_PIN PIN3_bm
// Takes inn messured value
// Calculates the temperature in celcius // Calculates the temperature in celcius
// Returns the thermistor themperature // Returns the thermistor themperature
float calculate_thermistor_temp(float readValue); float calculate_thermistor_temp(float readValue);
// Returns true if temperatur is higher then high_temp int
bool error_message(float thermistor_temp);
// Returns true if temperatur is higher than max_temp
bool voltage_threshold_bool(float thermistor_temp, uint8_t max_temp);
void alert_voltage_threshold_exceeded(bool voltage_threshold_bool);
// Initialise alarm GPIO
void init_alarm_gpio();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,52 +1,41 @@
#include "themistor-temp.h" #include "themistor-temp.h"
float calculate_thermistor_temp(float readValue){
void init_alarm_gpio(){ float R_therm;
PORTB.DIRSET = ALERT_PIN;
}
// The code is inspired by "Arduino thermistor guide" by valtentina Vogelman, 1 november 2023
// https://www.build-electronic-circuits.com/arduino-thermistor/
float calculate_thermistor_temp(float thermistor_voltage){
float R_thermistor;
float V_1; float V_1;
float ln; float ln;
float T_thermistor; float T_therm;
float V_thermistor; float V_therm;
#define V_TOT 3.3 uint8_t V_tot = 5;
// Calculate Voltage over thermistor // Calculate Voltage over thermistor
V_thermistor = (V_TOT/1024)*thermistor_voltage; V_therm = (V_tot/1024)*readValue;
// Voltage accross R_1 // Voltage accross R_1
V_1 = V_TOT - V_thermistor; V_1 = V_tot - V_therm;
// Calculate Thermistor resistanse // Calculate Thermistor resistanse
R_thermistor = (V_thermistor)/ (V_1 / R_1); R_therm = (V_therm)/ (V_1 / R_1);
// Steinhart-Harts formula // Steinhart-Harts formula
ln = log(R_thermistor/R_T0); ln = log(R_therm/R_t0);
T_thermistor = (1/ ((ln/B) + (1/T_0))); T_therm = (1/ ((ln/B) + (1/T_0)));
// Temperatur in celcius // Temperatur in celcius
T_thermistor -= 273.15; T_therm -= 273.15;
return T_thermistor; return T_therm;
} }
// returns error message if the messured thermistor temp is higher than // returns error message
// Choosen max_temp bool error_message(float thermistor_temp){
bool voltage_threshold_bool(float thermistor_temp, uint8_t max_temp){ // Max value
int high_temp = 0;
// Return true if temp is higher then max value // Return true if temp is higher then max value
return (thermistor_temp >= max_temp); if (thermistor_temp > high_temp){
} printf("Error");
return true;
//print if the maximum threshold is exceeded.
void alert_voltage_threshold_exceeded(bool voltage_threshold_bool){
if (voltage_threshold_bool){
//printf("Error: maximum temperature exceeded");
PORTB.OUTSET = ALERT_PIN;
} else{
PORTB.OUTCLR = ALERT_PIN;
} }
} else {
return false;
}
}

View File

@ -1,52 +0,0 @@
#include "themistor-temp.h"
void init_led(){
PORTB.DIRSET = ledpin;
}
https://www.build-electronic-circuits.com/arduino-thermistor/
float calculate_thermistor_temp(float thermistor_voltage){
float R_thermistor;
float V_1;
float ln;
float T_thermistor;
float V_thermistor;
#define V_TOT 5
// Calculate Voltage over thermistor
V_thermistor = (V_TOT/1024)*thermistor_voltage;
// Voltage accross R_1
V_1 = V_TOT - V_thermistor;
// Calculate Thermistor resistanse
R_thermistor = (V_thermistor)/ (V_1 / R_1);
// Steinhart-Harts formula
ln = log(R_thermistor/R_T0);
T_thermistor = (1/ ((ln/B) + (1/T_0)));
// Temperatur in celcius
T_thermistor -= 273.15;
return T_thermistor;
}
// returns error message if the messured thermistor temp is higher than
// Choosen max_temp
bool voltage_threshold_bool(float thermistor_temp, uint8_t max_temp){
// Return true if temp is higher then max value
return (thermistor_temp >= max_temp);
}
//print if the maximum threshold is exceeded.
void alert_voltage_threshold_exceeded(bool voltage_threshold_bool){
if (voltage_threshold_bool){
printf("Error: maximum temperature exceeded");
PORTB.OUTSET = ledpin;
} else{
PORTB.OUTCLR = ledpin;
}
}

View File

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

View File

@ -2,58 +2,40 @@
#include "uart.h" #include "uart.h"
void ADC0_init(void) { void ADC0_init(void) {
/* Initializing ADC0 pin*/ /* Initializing ADC0 pin*/
/*Voltage reading on pin pd2*/ PORTD.PIN6CTRL &= ~PORT_ISC_gm;
PORTD.PIN2CTRL &= ~PORT_ISC_gm; PORTD.PIN6CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */
PORTD.PIN2CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */ PORTD.PIN6CTRL &= PORT_PULLUPEN_bm;
PORTD.PIN2CTRL &= PORT_PULLUPEN_bm;
/* Thermistor */ ADC0.CTRLC = ADC_PRESC_DIV4_gc;
PORTD.PIN3CTRL &= ~PORT_ISC_gm; VREF.ADC0REF = VREF_REFSEL_VDD_gc; /* Internal reference */
PORTD.PIN3CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */ ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */
PORTD.PIN3CTRL &= PORT_PULLUPEN_bm; | ADC_RESSEL_10BIT_gc; /* 10-bit mode */
/* Select ADC channel */
ADC0.CTRLC = ADC_PRESC_DIV4_gc; ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc;
VREF.ADC0REF = VREF_REFSEL_VDD_gc; /* Internal reference */
ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */
| ADC_RESSEL_10BIT_gc; /* 10-bit mode */
/* Select ADC channel */
ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc;
} }
uint16_t ADC0_read(void) { uint16_t ADC0_read(void) {
/* Start ADC conversion */ /* Start ADC conversion */
ADC0.COMMAND = ADC_STCONV_bm; ADC0.COMMAND = ADC_STCONV_bm;
/* Wait until ADC conversion done */ /* Wait until ADC conversion done */
while (!(ADC0.INTFLAGS & ADC_RESRDY_bm)) { while (!(ADC0.INTFLAGS & ADC_RESRDY_bm)) {
; ;
} }
// Clear the interrupt flag by writing 1: // Clear the interrupt flag by writing 1:
ADC0.INTFLAGS = ADC_RESRDY_bm; ADC0.INTFLAGS = ADC_RESRDY_bm;
return ADC0.RES; return ADC0.RES;
} }
uint16_t thermistor_voltage_read() { uint16_t diode_voltage_read() {
/* Gets value for the diode */ /* Gets value for the diode */
ADC0.MUXPOS = 0x03; // Read PD3 uint8_t adcVal = ADC0_read();
uint16_t adc_val = ADC0_read();
return adc_val; return adcVal;
}
// Gets the value over thermistor
uint16_t external_voltage_read() {
ADC0.MUXPOS = 0x02; // Read PD6
uint16_t adc_val = ADC0_read();
return adc_val;
} }
uint16_t internal_voltage_read() { uint16_t internal_voltage_read() {
/* Gets value for the internal voltage reffreance*/ /* Gets value for the internal voltage reffreance*/
ADC0.MUXPOS = 0x44; VREF.ADC0REF = VREF_REFSEL_VDD_gc;
uint8_t adc_val = ADC0_read();
return adc_val * 10;
} }

View File

@ -20,9 +20,9 @@
*/ */
/* /*
* File: Voltage.h * File:
* Author: Sebastian Hernø Gabrielli, Helle Augland Grasmo, Ina Min Rørnes * Author:
* Comments: * Comments:
* Revision history: * Revision history:
*/ */
@ -54,13 +54,7 @@ extern "C" {
uint16_t ADC0_read(void); uint16_t ADC0_read(void);
// Gets the value from sensor and internal voltage // Gets the value from sensor and internal voltage
uint16_t internal_voltage_read(); uint16_t internal_voltage_read();
// Gets the value over thermistor uint16_t diode_voltage_read();
uint16_t thermistor_voltage_read();
// Gets internal voltage
uint16_t external_voltage_read();
// Converts value to voltage
uint16_t convert_to_voltage(uint16_t adc_val);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */