Compare commits

...

64 Commits

Author SHA1 Message Date
9b2e729683 Merge remote-tracking branch 'origin/combine-all' 2024-05-03 19:45:53 +02:00
c5521a08fa Add sources 2024-05-03 17:16:29 +02:00
Sebastian H. Gabrielli
25d27c1e1b Add alarm code 2024-04-30 16:43:07 +02:00
Sebastian H. Gabrielli
cd65b8fd1f Lots of bug fixes, everything works 2024-04-30 16:17:52 +02:00
Sebastian H. Gabrielli
bc9e3f6885 Update the sample rate when the config is changed 2024-04-30 12:00:29 +02:00
Sebastian H. Gabrielli
d215e204d4 Rename files & re-factor to pretty up project 2024-04-30 11:37:21 +02:00
Sebastian H. Gabrielli
458ae00f24 Rename fanspeeeed to fanspeed 2024-04-30 11:26:08 +02:00
Sebastian H. Gabrielli
b858d29ce8 Make read bulk fan speed read correctly from the array 2024-04-30 11:21:26 +02:00
Sebastian H. Gabrielli
1671476f41 Move index incrementation over storage
This avoids the problem where the current index has outdated data.
It does however mean the first piece of stored data is empty, but it is
a worthwile tradeoff.
2024-04-30 11:11:32 +02:00
Sebastian H. Gabrielli
bcd631001b Current fan speed now returns the last fan speed measured 2024-04-30 11:10:26 +02:00
Sebastian H. Gabrielli
dd53b4dbf2 Fan data is now saved in the array
Closes #9
2024-04-30 11:08:03 +02:00
Sebastian H. Gabrielli
275c40498f Merge branch 'fanspeed-comparator' 2024-04-30 10:59:47 +02:00
495cbbba44 Merge pull request 'Merge I2C command handler into main' (#18) from output-fan-data-over-i2c into main
Reviewed-on: #18
2024-04-30 08:37:27 +00:00
Sebastian H. Gabrielli
91cffd78f4 Move data initialization into write, as it is only needed there 2024-04-28 20:46:50 +02:00
Sebastian H. Gabrielli
4666f79dd3 Init the fan history as 0 and add Ina to authors in main 2024-04-28 20:23:17 +02:00
Sebastian H. Gabrielli
d31f03b6c7 Delete unused EEPROM code & update comments
We don't use a lot of the written EEPROM code.
This unused code has now been deleted
2024-04-28 20:21:56 +02:00
Sebastian H. Gabrielli
6acdb2beb6 Rename receive buffer for better clarity & add comment 2024-04-28 20:14:27 +02:00
Sebastian H. Gabrielli
ba54ff20ef Fix master slave naming and remove dead code 2024-04-28 20:12:24 +02:00
Sebastian H. Gabrielli
8f61e6b998 Validate that we do not overflow the receive buffer
Closes #20
2024-04-28 20:10:01 +02:00
Sebastian H. Gabrielli
ae1c50ee09 Writing config works 2024-04-27 15:39:46 +02:00
Sebastian H. Gabrielli
f654ae96f6 Add case structure for better handling of future config params
A case structure to send the correct config value has been added.
2024-04-27 14:55:44 +02:00
Sebastian H. Gabrielli
dd624d4d48 Reading config parameter works 2024-04-27 14:52:46 +02:00
Sebastian H. Gabrielli
fb24365469 Prepare for write config 2024-04-24 15:50:58 +02:00
Sebastian H. Gabrielli
b8d19731ad Clear fan data works 2024-04-24 15:43:27 +02:00
Sebastian H. Gabrielli
822845cc7a Read temperature command works 2024-04-24 13:40:45 +02:00
Sebastian H. Gabrielli
1eaeeef9a0 Read temperature over I2C works
Has not been tested with thermistor as I didn't have the hardware.
Testing with a hardcoded positive number works.
2024-04-24 13:24:15 +02:00
Sebastian H. Gabrielli
d964ebea37 Handle read single fan speed 2024-04-23 15:45:41 +02:00
Sebastian H. Gabrielli
bf3753a012 I2C bulk fan read works 2024-04-23 15:31:32 +02:00
e6a3815dc2 Merge branch 'thermistor-temp' into main 2024-04-16 18:00:41 +02:00
933949dcff Fix variable name and led 2024-04-16 17:24:54 +02:00
Sebastian H. Gabrielli
a9fec3f661 Merge branch 'command-handler' 2024-04-16 17:02:59 +02:00
Sebastian H. Gabrielli
5ea7be0728 Explain pos variable 2024-04-16 16:20:32 +02:00
Sebastian H. Gabrielli
b1ca4c05bf Rename foo in command handler to a more descirptive name
"foo" -> "command_byte"
2024-04-16 16:17:35 +02:00
9f27bc8454 Fix names 2024-04-16 15:56:51 +02:00
Sebastian H. Gabrielli
7e59368252 Implement handling reading more than one byte 2024-04-16 15:35:43 +02:00
Sebastian H. Gabrielli
9fd877aab5 Command handing works!
The command handler can now receive a command, then it can run a
function / return some data based on it.
2024-04-16 15:30:38 +02:00
1b8ca0c7f6 Merge branch 'voltage-reading' into main 2024-04-09 16:20:46 +02:00
ca28e23b70 Fixed header, comments and Muxpos 2024-04-09 16:13:04 +02:00
7f3c1fa7c1 Commented the code 2024-04-09 15:58:17 +02:00
ee6e6a6d07 Make voltage converter 2024-04-09 15:54:42 +02:00
b0c0efa4fc Delete a line 2024-04-09 15:29:32 +02:00
91ee8b1af8 Add muxpos to internal voltage 2024-04-09 14:56:14 +02:00
ae28be55d3 Fix muxpos 2024-04-09 14:36:11 +02:00
953034cab8 Add thermistor adc reading 2024-04-09 13:50:02 +02:00
a9f5c42243 Fix comments 2024-04-09 13:37:38 +02:00
81ebcd2c79 Merge branch 'eeprom' into main 2024-04-09 12:33:46 +02:00
Sebastian H. Gabrielli
8e3607cbda Store and hold I2C data
I2C data is now stored until stop is sent, then printed.
2024-03-20 13:24:55 +01:00
Sebastian H. Gabrielli
4e0b810346 Functioning I2C slave data receive
The slave now receives and prints data successfully.
2024-03-20 13:00:40 +01:00
Sebastian H. Gabrielli
e1c607514b Merge branch 'add-uart' into command-handler 2024-03-20 11:20:22 +01:00
849c719b59 Change error temp
Make it able to innputt a chosen max temp
2024-03-20 11:06:46 +01:00
7bcec13f6e Merge two lines 2024-03-20 10:53:46 +01:00
5b884a50e2 Fix return error 2024-03-20 10:52:38 +01:00
bf4f3aaa81 fix return array 2024-03-19 14:02:10 +01:00
a8be04b583 Fix names 2024-03-13 12:03:40 +01:00
4a8b6137d1 Change namingsheme 2024-03-13 11:48:58 +01:00
c48f87bcea write to update, and uart away. 2024-03-13 10:34:46 +01:00
6d6ea9606e fix comments and variable names 2024-03-06 20:05:52 +01:00
bd424b78d1 fix source files 2024-03-06 15:49:48 +01:00
7008037de9 Fix EEPROM is ready 2024-03-06 15:27:30 +01:00
27d8d44d34 Fix eeprom
EEPROM works
2024-03-06 15:24:34 +01:00
Sebastian H. Gabrielli
ac78f33c98 Create basic i2c skeleton with interrupts
I2C init function has been created.
Interrupts have been made for each I2C interrupt. Which interrupt does
what is currently unknown.

See #1
2024-03-06 14:58:49 +01:00
8b73051ece add eeprom code, not work 2024-03-06 14:49:16 +01:00
Sebastian H. Gabrielli
45bc800397 Implement command parser up to second parameter
The command parser has been implemented for all options other than the
config option.

See #1
2024-03-06 13:51:52 +01:00
Sebastian H. Gabrielli
06d5c3c464 Create basic command handler skeleton
The groundwork for the command handler has been created.
2024-03-06 13:20:10 +01:00
16 changed files with 953 additions and 138 deletions

View File

@ -0,0 +1,307 @@
#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

@ -0,0 +1,89 @@
/*
* 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 */

42
prosjekt.X/eeprom.c Normal file
View File

@ -0,0 +1,42 @@
#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;
}

46
prosjekt.X/eeprom.h Normal file
View File

@ -0,0 +1,46 @@
/*
* 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_speeeed.h" #include "fan-speed.h"
#include "uart.h" #include "uart.h"
uint16_t timer_period_ms = 1; uint16_t timer_period_ms = 1000;
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,18 +10,21 @@ 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 timer_period) { void TCA0_update_period(uint16_t period_ms) {
TCA0.SINGLE.PERBUF = (F_CPU * (1 / timer_period) / 1024); /* F_CPU * F_IRQ / TCA_prescaler */ float period_s = period_ms / 1E3; // Convert the ms to s
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 / time_ms); fan_speed = (edge_counter / (float)((float)time_ms/1000.0) );
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;
@ -98,8 +101,25 @@ 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);
RPM_calculation(fan2_edge_counter,timer_period_ms); // Increment the index, or reset if it is at the top
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,40 +15,47 @@ 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

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

@ -0,0 +1,125 @@
#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();
}

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 <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,43 +1,83 @@
/* /*
* File: main.c * File: main.c
* Author: Sebastian H. Gabrielli * Author: Sebastian H. Gabrielli, Helle Augland Grasmo, Ina Min Rørnes
* *
* Created on March 6, 2024, 12:34 PM * Created on March 6, 2024, 12:34 PM
*/ */
#include "voltage.h" #define F_CPU 4E6
#include "uart.h"
#define RTC_PERIOD (511) // Include AVR specific libs
#define DELAY_TIME 1000 #include <avr/interrupt.h>
#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 "uart.h" #include <stdint.h>
#include <util/delay.h>
#include <stdint.h> // Include custom source files
#include "themistor-temp.h" #include "eeprom.h"
#include "fan_speeeed.h" #include "voltage.h"
#include "command-handler.h"
#define F_CPU 4000000UL #include "i2c.h"
#include "themistor-temp.h"
#include "fan-speed.h"
// Only enable UART when required for debugging
int main(void) { #ifdef ENABLE_UART
#include "uart.h"
init_uart((uint16_t)9600); #endif
stdout = &USART_stream;
init_TCA0(); // Fan history variables
init_fan_gpio(); volatile uint16_t fan1_history[512] = {0};
init_AC0(); volatile uint16_t fan2_history[512] = {0};
init_AC1(); // Fan history index variable
sei(); volatile uint16_t fan1_history_index = 0;
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,8 +6,11 @@
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"
@ -25,8 +28,11 @@
<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,24 +13,34 @@ 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>
#define R_t0 10000 #include <avr/io.h>
#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,41 +1,52 @@
#include "themistor-temp.h" #include "themistor-temp.h"
float calculate_thermistor_temp(float readValue){
float R_therm; void init_alarm_gpio(){
float V_1; PORTB.DIRSET = ALERT_PIN;
float ln;
float T_therm;
float V_therm;
uint8_t V_tot = 5;
// Calculate Voltage over thermistor
V_therm = (V_tot/1024)*readValue;
// Voltage accross R_1
V_1 = V_tot - V_therm;
// Calculate Thermistor resistanse
R_therm = (V_therm)/ (V_1 / R_1);
// Steinhart-Harts formula
ln = log(R_therm/R_t0);
T_therm = (1/ ((ln/B) + (1/T_0)));
// Temperatur in celcius
T_therm -= 273.15;
return T_therm;
} }
// returns error message // The code is inspired by "Arduino thermistor guide" by valtentina Vogelman, 1 november 2023
bool error_message(float thermistor_temp){ // https://www.build-electronic-circuits.com/arduino-thermistor/
// Max value
int high_temp = 0; float calculate_thermistor_temp(float thermistor_voltage){
float R_thermistor;
float V_1;
float ln;
float T_thermistor;
float V_thermistor;
#define V_TOT 3.3
// 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 true if temp is higher then max value
if (thermistor_temp > high_temp){ return (thermistor_temp >= max_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

@ -0,0 +1,52 @@
#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,40 +2,58 @@
#include "uart.h" #include "uart.h"
void ADC0_init(void) { void ADC0_init(void) {
/* Initializing ADC0 pin*/ /* Initializing ADC0 pin*/
PORTD.PIN6CTRL &= ~PORT_ISC_gm; /*Voltage reading on pin pd2*/
PORTD.PIN6CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */ PORTD.PIN2CTRL &= ~PORT_ISC_gm;
PORTD.PIN6CTRL &= PORT_PULLUPEN_bm; PORTD.PIN2CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */
PORTD.PIN2CTRL &= PORT_PULLUPEN_bm;
ADC0.CTRLC = ADC_PRESC_DIV4_gc; /* Thermistor */
VREF.ADC0REF = VREF_REFSEL_VDD_gc; /* Internal reference */ PORTD.PIN3CTRL &= ~PORT_ISC_gm;
ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */ PORTD.PIN3CTRL |= PORT_ISC_INPUT_DISABLE_gc; /* Disable */
| ADC_RESSEL_10BIT_gc; /* 10-bit mode */ PORTD.PIN3CTRL &= PORT_PULLUPEN_bm;
/* Select ADC channel */
ADC0.MUXPOS = ADC_MUXPOS_AIN6_gc; ADC0.CTRLC = ADC_PRESC_DIV4_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 diode_voltage_read() { uint16_t thermistor_voltage_read() {
/* Gets value for the diode */ /* Gets value for the diode */
uint8_t adcVal = ADC0_read(); ADC0.MUXPOS = 0x03; // Read PD3
uint16_t adc_val = ADC0_read();
return adcVal; return adc_val;
}
// 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*/
VREF.ADC0REF = VREF_REFSEL_VDD_gc; ADC0.MUXPOS = 0x44;
uint8_t adc_val = ADC0_read();
return adc_val * 10;
} }

View File

@ -20,9 +20,9 @@
*/ */
/* /*
* File: * File: Voltage.h
* Author: * Author: Sebastian Hernø Gabrielli, Helle Augland Grasmo, Ina Min Rørnes
* Comments: * Comments:
* Revision history: * Revision history:
*/ */
@ -54,7 +54,13 @@ 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();
uint16_t diode_voltage_read(); // Gets the value over thermistor
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 */