Merge branch 'command-handler'
This commit is contained in:
commit
a9fec3f661
158
prosjekt.X/command-handler.c
Normal file
158
prosjekt.X/command-handler.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#include "command-handler.h"
|
||||||
|
|
||||||
|
// Initialize empty, global command context
|
||||||
|
volatile command_context_t context = {UNKNOWN_COMMAND, SRC_NONE, FAN_NONE,
|
||||||
|
CNF_NONE};
|
||||||
|
|
||||||
|
void parse_command(uint8_t command[], uint8_t command_len) {
|
||||||
|
///////////////////////
|
||||||
|
// Command selection //
|
||||||
|
///////////////////////
|
||||||
|
|
||||||
|
// Validate that we have a command
|
||||||
|
if (command_len < 1) {
|
||||||
|
context.command = UNKNOWN_COMMAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the first byte, which contains the command
|
||||||
|
uint8_t command_byte = command[0];
|
||||||
|
|
||||||
|
// Figure out which command to run
|
||||||
|
switch (command_byte) {
|
||||||
|
case 0x11: // Read config
|
||||||
|
context.command = READ_CONFIG;
|
||||||
|
break;
|
||||||
|
case 0x12: // Read voltage
|
||||||
|
context.command = READ_VOLTAGE;
|
||||||
|
break;
|
||||||
|
case 0x13: // Read temperature
|
||||||
|
context.command = READ_TERMPERATURE;
|
||||||
|
break;
|
||||||
|
case 0x14: // Read current fan speed
|
||||||
|
context.command = READ_FAN_SPEED;
|
||||||
|
break;
|
||||||
|
case 0x15: // Read bulk fan speed
|
||||||
|
context.command = READ_BULK_FAN_SPEED;
|
||||||
|
break;
|
||||||
|
case 0x21: // Write config
|
||||||
|
context.command = WRITE_CONFIG;
|
||||||
|
break;
|
||||||
|
case 0x22: // Clear stored fan speed data
|
||||||
|
context.command = CLEAR_BULK_FAN_SPEED;
|
||||||
|
break;
|
||||||
|
default: // Unrecognized command
|
||||||
|
context.command = UNKNOWN_COMMAND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// First parameter selection //
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
// Check if the command does not require a parameter. If it does not, return.
|
||||||
|
if (context.command == READ_TERMPERATURE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that we have a first parameter, else requirements for command are
|
||||||
|
// not fulfilled. return unknown command.
|
||||||
|
if (command_len < 2) {
|
||||||
|
context.command = UNKNOWN_COMMAND;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the parameter
|
||||||
|
uint8_t param = command[1];
|
||||||
|
|
||||||
|
// Extract the parameter. Parameter is dependent on command
|
||||||
|
switch (command[0]) {
|
||||||
|
// Configuration parameters
|
||||||
|
case 0x11: // Read config
|
||||||
|
case 0x21: // Write config
|
||||||
|
// TODO: Handle parameters for config
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Voltage parameters
|
||||||
|
case 0x12: // Read voltage
|
||||||
|
if (param == 0x01) {
|
||||||
|
context.src_voltage = SRC_INTERNAL;
|
||||||
|
} else if (param == 0x02) {
|
||||||
|
context.src_voltage = SRC_EXTRNAL;
|
||||||
|
} else if (param == 0x03) {
|
||||||
|
context.src_voltage = SRC_THERMISTOR;
|
||||||
|
} else {
|
||||||
|
context.src_voltage = SRC_NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Fan parameters
|
||||||
|
case 0x14: // Read current fan speed
|
||||||
|
case 0x15: // Read bulk fan speed
|
||||||
|
case 0x22: // Clear stored fan speed data
|
||||||
|
context.command = READ_BULK_FAN_SPEED;
|
||||||
|
if (param == 0x01) {
|
||||||
|
context.fan = FAN1;
|
||||||
|
} else if (param == 0x02) {
|
||||||
|
context.fan = FAN2;
|
||||||
|
} else {
|
||||||
|
context.fan = FAN_NONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// This should never be reached
|
||||||
|
default: // Unrecognized command
|
||||||
|
context.command = UNKNOWN_COMMAND;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Second parameter selection //
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// Check if the command does not require a second parameter. If it does not,
|
||||||
|
// return. Only config write requires a second parameter.
|
||||||
|
if (context.command != WRITE_CONFIG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that we have a first parameter, else requirements for command are
|
||||||
|
// not fulfilled. return unknown command.
|
||||||
|
if (command_len < 3) {
|
||||||
|
context.command = UNKNOWN_COMMAND;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the parameter
|
||||||
|
param = command[2];
|
||||||
|
|
||||||
|
// TODO: Handle the config parameters
|
||||||
|
|
||||||
|
// exit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t route_command(int pos) {
|
||||||
|
switch (context.command) {
|
||||||
|
WRITE_CONFIG:
|
||||||
|
return WRITE_CONFIG;
|
||||||
|
break;
|
||||||
|
READ_CONFIG:
|
||||||
|
return READ_CONFIG;
|
||||||
|
break;
|
||||||
|
READ_VOLTAGE:
|
||||||
|
return READ_VOLTAGE;
|
||||||
|
break;
|
||||||
|
READ_TERMPERATURE:
|
||||||
|
return READ_TERMPERATURE;
|
||||||
|
break;
|
||||||
|
READ_FAN_SPEED:
|
||||||
|
return READ_FAN_SPEED;
|
||||||
|
break;
|
||||||
|
CLEAR_BULK_FAN_SPEED:
|
||||||
|
return CLEAR_BULK_FAN_SPEED;
|
||||||
|
break;
|
||||||
|
UNKNOWN_COMMAND:
|
||||||
|
return 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
prosjekt.X/command-handler.h
Normal file
74
prosjekt.X/command-handler.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* File: command-handler.h
|
||||||
|
* Author: Sebastian H. Gabrielli
|
||||||
|
*
|
||||||
|
* Created on March 6, 2024, 12:43 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMAND_HANDLER_H
|
||||||
|
#define COMMAND_HANDLER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Enum of all valid command types
|
||||||
|
typedef enum {
|
||||||
|
WRITE_CONFIG, // Change the configuration
|
||||||
|
READ_CONFIG, // Read, and print the current configuration
|
||||||
|
READ_VOLTAGE, // Read, and print a voltage
|
||||||
|
READ_TERMPERATURE, // Read, and print the temperature
|
||||||
|
READ_FAN_SPEED, // Read, and print the current fan speed
|
||||||
|
READ_BULK_FAN_SPEED, // Read, and print the stored back fan speed data
|
||||||
|
CLEAR_BULK_FAN_SPEED, // Clear the buffer of stored fan speed data
|
||||||
|
UNKNOWN_COMMAND // An unrecognized command has been sent
|
||||||
|
} command_t;
|
||||||
|
|
||||||
|
// Enum of all valid voltage sources
|
||||||
|
typedef enum {
|
||||||
|
SRC_INTERNAL, // Internal volage
|
||||||
|
SRC_EXTRNAL, // External voltage
|
||||||
|
SRC_THERMISTOR, // Thermistor voltage
|
||||||
|
SRC_NONE // No voltage source selected
|
||||||
|
} src_voltage_t;
|
||||||
|
|
||||||
|
// Enum of all valid config options
|
||||||
|
// TODO: Move into config header file
|
||||||
|
typedef enum {
|
||||||
|
CNF_NONE, // No config option
|
||||||
|
} config_option_t;
|
||||||
|
|
||||||
|
// Enum of all valid fans
|
||||||
|
// TODO: Consider moving into it's own fan page
|
||||||
|
typedef enum {
|
||||||
|
FAN1, // The first fan
|
||||||
|
FAN2, // The second fan
|
||||||
|
FAN_NONE // No fan
|
||||||
|
} fans_t;
|
||||||
|
|
||||||
|
// Struct with command context
|
||||||
|
typedef struct {
|
||||||
|
command_t command; // The command to execute
|
||||||
|
src_voltage_t src_voltage; // The selected voltage source
|
||||||
|
fans_t fan; // The selected fan
|
||||||
|
config_option_t conf; // The configuration option to cange
|
||||||
|
// TODO: Add config value field for writing
|
||||||
|
} command_context_t;
|
||||||
|
|
||||||
|
// Parses the input string and outputs one of the valid commands
|
||||||
|
void parse_command(uint8_t *command, uint8_t command_len);
|
||||||
|
|
||||||
|
// Routes the provided command to the appropriate function to handle it
|
||||||
|
// If the command is a read command it then returns the current byte.
|
||||||
|
// The position is the byte to read when multiple bytes are being read.
|
||||||
|
uint8_t route_command(int pos);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* COMMAND_HANDLER_H */
|
||||||
117
prosjekt.X/i2c.c
Normal file
117
prosjekt.X/i2c.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include "i2c.h"
|
||||||
|
#include "command-handler.h"
|
||||||
|
|
||||||
|
// Basic I2C handling structure is heavily inspired by:
|
||||||
|
// https://github.com/microchip-pic-avr-examples/avr128db48-bare-metal-twi-mplab/blob/master/twi-client.X/peripherals/TWI/TWI_client.c
|
||||||
|
|
||||||
|
// We need to keep track of if the stop confition is in regards to a write or
|
||||||
|
// read request
|
||||||
|
volatile bool last_action_write = false;
|
||||||
|
|
||||||
|
volatile uint8_t i2c_recv[I2C_RECV_BUF_SIZE] = {
|
||||||
|
0}; // Arbitrary length array to hold the received
|
||||||
|
// data, longer than max expected command
|
||||||
|
volatile uint8_t i2c_recv_len = 0;
|
||||||
|
|
||||||
|
void init_i2c(void) {
|
||||||
|
// Pin setup
|
||||||
|
PORTA.DIRSET = PIN2_bm | PIN3_bm;
|
||||||
|
PORTA.PINCTRLUPD = PIN2_bm | PIN3_bm;
|
||||||
|
|
||||||
|
// Enable operating in debug
|
||||||
|
TWI0.DBGCTRL = TWI_DBGRUN_bm;
|
||||||
|
|
||||||
|
// Initialize the control A register
|
||||||
|
TWI0.CTRLA = TWI_INPUTLVL_I2C_gc // I2C voltage transition level
|
||||||
|
| TWI_SDASETUP_4CYC_gc // Four clock cycles setup time
|
||||||
|
| TWI_SDAHOLD_50NS_gc // 50ns SDA hold time
|
||||||
|
| TWI_FMPEN_OFF_gc // Standard SPI timing
|
||||||
|
;
|
||||||
|
|
||||||
|
// The device's slave address
|
||||||
|
TWI0.SADDR = 0x42;
|
||||||
|
|
||||||
|
// Enable acting as a slave
|
||||||
|
TWI0.SCTRLA = TWI_DIEN_bm // Enable data interrupt
|
||||||
|
| TWI_APIEN_bm // Enable more interrupts
|
||||||
|
| TWI_PIEN_bm // Enable stop flag interrupt
|
||||||
|
| PIN2_bm // Respond to all TWI addresses
|
||||||
|
| TWI_ENABLE_bm // Enable acting as a slave
|
||||||
|
;
|
||||||
|
|
||||||
|
// Reset the received stuff
|
||||||
|
i2c_reset_recv();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset received counter and clear receive buffer
|
||||||
|
void i2c_reset_recv() {
|
||||||
|
i2c_recv_len = 0;
|
||||||
|
for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) {
|
||||||
|
i2c_recv[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_write_handler(uint8_t data) {
|
||||||
|
last_action_write = true;
|
||||||
|
i2c_recv[i2c_recv_len] = data;
|
||||||
|
i2c_recv_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_read_handler() {
|
||||||
|
last_action_write = false;
|
||||||
|
TWI0.SDATA = route_command(i2c_recv_len);
|
||||||
|
i2c_recv_len++; // Increment the counter of the current amount of requests
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_stop_handler() {
|
||||||
|
if (last_action_write) {
|
||||||
|
// Parse the received command data
|
||||||
|
parse_command(i2c_recv, i2c_recv_len);
|
||||||
|
}
|
||||||
|
// Reset the buffer for future transmissions
|
||||||
|
i2c_reset_recv();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address received
|
||||||
|
ISR(TWI0_TWIS_vect) {
|
||||||
|
// Disable interrupts while handling I2C
|
||||||
|
cli();
|
||||||
|
|
||||||
|
// Check for the data interrupt flag
|
||||||
|
if (TWI0.SSTATUS & TWI_DIF_bm) {
|
||||||
|
uint8_t data = 0;
|
||||||
|
|
||||||
|
if (((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0) {
|
||||||
|
// Data write Master -> Slave
|
||||||
|
data = TWI0.SDATA;
|
||||||
|
|
||||||
|
// Send the data to the write handler
|
||||||
|
i2c_write_handler(data);
|
||||||
|
} else {
|
||||||
|
// Data read Master <- Slave
|
||||||
|
i2c_read_handler();
|
||||||
|
// data = TWI0.SDATA;
|
||||||
|
// TWI0.SDATA = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acknowledge having received
|
||||||
|
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for address match or STOP
|
||||||
|
if (TWI0.SSTATUS & TWI_APIF_bm) {
|
||||||
|
|
||||||
|
if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
|
||||||
|
// Address match, just send ack
|
||||||
|
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
|
||||||
|
} else {
|
||||||
|
// STOP condition received
|
||||||
|
i2c_stop_handler();
|
||||||
|
// Send ACK
|
||||||
|
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable interrupts
|
||||||
|
sei();
|
||||||
|
}
|
||||||
36
prosjekt.X/i2c.h
Normal file
36
prosjekt.X/i2c.h
Normal 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 "uart.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <util/twi.h>
|
||||||
|
|
||||||
|
// Received data info
|
||||||
|
#define I2C_RECV_BUF_SIZE 64
|
||||||
|
|
||||||
|
// Reset recv to initial state
|
||||||
|
void i2c_reset_recv();
|
||||||
|
|
||||||
|
// Initialize the I2C bus
|
||||||
|
void init_i2c(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* I2C_H */
|
||||||
@ -4,31 +4,35 @@
|
|||||||
*
|
*
|
||||||
* Created on March 6, 2024, 12:34 PM
|
* Created on March 6, 2024, 12:34 PM
|
||||||
*/
|
*/
|
||||||
#include "voltage.h"
|
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
|
#include "voltage.h"
|
||||||
#define RTC_PERIOD (511)
|
#define RTC_PERIOD (511)
|
||||||
#define DELAY_TIME 1000
|
#define DELAY_TIME 1000
|
||||||
#include <avr/io.h>
|
|
||||||
#include <avr/interrupt.h>
|
|
||||||
#include <util/delay.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include "eeprom.h"
|
#include "eeprom.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#define F_CPU 4E6
|
#define F_CPU 4E6
|
||||||
|
#include "command-handler.h"
|
||||||
|
#include "i2c.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
|
#include <avr/io.h>
|
||||||
#include <util/delay.h>
|
#include <util/delay.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
ADC0_init();
|
|
||||||
init_uart((uint16_t)9600);
|
init_uart((uint16_t)9600);
|
||||||
|
init_i2c();
|
||||||
stdout = &USART_stream;
|
stdout = &USART_stream;
|
||||||
|
|
||||||
|
PORTB.DIRSET = PIN3_bm;
|
||||||
|
|
||||||
|
sei();
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
uint16_t adcVal = ADC0_read();
|
// uint16_t adcVal = ADC0_read();
|
||||||
printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal);
|
// printf("The values: \n%u , %u\n",VREF_REFSEL_VDD_gc , adcVal);
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,6 +4,8 @@
|
|||||||
<logicalFolder name="HeaderFiles"
|
<logicalFolder name="HeaderFiles"
|
||||||
displayName="Header Files"
|
displayName="Header Files"
|
||||||
projectFiles="true">
|
projectFiles="true">
|
||||||
|
<itemPath>command-handler.h</itemPath>
|
||||||
|
<itemPath>i2c.h</itemPath>
|
||||||
<itemPath>uart.h</itemPath>
|
<itemPath>uart.h</itemPath>
|
||||||
<itemPath>eeprom.h</itemPath>
|
<itemPath>eeprom.h</itemPath>
|
||||||
<itemPath>voltage.h</itemPath>
|
<itemPath>voltage.h</itemPath>
|
||||||
@ -16,6 +18,8 @@
|
|||||||
displayName="Source Files"
|
displayName="Source Files"
|
||||||
projectFiles="true">
|
projectFiles="true">
|
||||||
<itemPath>main.c</itemPath>
|
<itemPath>main.c</itemPath>
|
||||||
|
<itemPath>command-handler.c</itemPath>
|
||||||
|
<itemPath>i2c.c</itemPath>
|
||||||
<itemPath>uart.c</itemPath>
|
<itemPath>uart.c</itemPath>
|
||||||
<itemPath>eeprom.c</itemPath>
|
<itemPath>eeprom.c</itemPath>
|
||||||
<itemPath>voltage.c</itemPath>
|
<itemPath>voltage.c</itemPath>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user