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

@ -30,13 +30,6 @@ extern "C" {
*/ */
// 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();

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 <avr/interrupt.h>
#include <util/delay.h> #include <util/delay.h>
#include <string.h>
// Include standard C libraries
#include <stdbool.h> #include <stdbool.h>
//#define F_CPU 4E6
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <stdint.h>
// Include custom source files
#include "eeprom.h"
#include "voltage.h"
#include "command-handler.h"
#include "i2c.h"
#include "themistor-temp.h"
#include "fan-speed.h"
// Only enable UART when required for debugging
#ifdef ENABLE_UART
#include "uart.h" #include "uart.h"
#endif #include <util/delay.h>
#include <stdint.h>
#include "themistor-temp.h"
#include "fan_speeeed.h"
// Fan history variables #define F_CPU 4000000UL
volatile uint16_t fan1_history[512] = {0};
volatile uint16_t fan2_history[512] = {0};
// Fan history index variable
volatile uint16_t fan1_history_index = 0;
volatile uint16_t fan2_history_index = 0;
// 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 int main(void) {
#ifdef ENABLE_UART
init_uart((uint16_t)9600); init_uart((uint16_t)9600);
stdout = &USART_stream; stdout = &USART_stream;
#endif init_TCA0();
init_fan_gpio();
// Read the stored config struct init_AC0();
config = read_struct_from_EEPROM(); init_AC1();
// Enable interrupts
sei(); sei();
while (1) { 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

@ -18,29 +18,19 @@ extern "C" {
#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
#define ALERT_PIN PIN3_bm
// Takes inn messured value // 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){ else {
if (voltage_threshold_bool){ return false;
//printf("Error: maximum temperature exceeded");
PORTB.OUTSET = ALERT_PIN;
} else{
PORTB.OUTCLR = ALERT_PIN;
} }
} }

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

@ -3,21 +3,14 @@
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 */
PORTD.PIN3CTRL &= ~PORT_ISC_gm;
PORTD.PIN3CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */
PORTD.PIN3CTRL &= PORT_PULLUPEN_bm;
ADC0.CTRLC = ADC_PRESC_DIV4_gc; ADC0.CTRLC = ADC_PRESC_DIV4_gc;
VREF.ADC0REF = VREF_REFSEL_VDD_gc; /* Internal reference */ VREF.ADC0REF = VREF_REFSEL_VDD_gc; /* Internal reference */
ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */ ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */
| ADC_RESSEL_10BIT_gc; /* 10-bit mode */ | ADC_RESSEL_10BIT_gc; /* 10-bit mode */
/* Select ADC channel */ /* Select ADC channel */
ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc; ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc;
} }
@ -34,26 +27,15 @@ uint16_t ADC0_read(void) {
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,8 +20,8 @@
*/ */
/* /*
* 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 */