From 06d5c3c464910ec52ae6a98906b13ce41129ef7f Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Wed, 6 Mar 2024 13:20:10 +0100 Subject: [PATCH 1/9] Create basic command handler skeleton The groundwork for the command handler has been created. --- prosjekt.X/command-handler.c | 5 ++ prosjekt.X/command-handler.h | 65 +++++++++++++++++++++++++ prosjekt.X/nbproject/configurations.xml | 2 + 3 files changed, 72 insertions(+) create mode 100644 prosjekt.X/command-handler.c create mode 100644 prosjekt.X/command-handler.h diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c new file mode 100644 index 0000000..91e537c --- /dev/null +++ b/prosjekt.X/command-handler.c @@ -0,0 +1,5 @@ +#include "command-handler.h" + +command_t parse_command(char *command_str); + +void route_command(command_t command); diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h new file mode 100644 index 0000000..7d8c304 --- /dev/null +++ b/prosjekt.X/command-handler.h @@ -0,0 +1,65 @@ +/* + * 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 + +// Enum of all valid command types +typedef enum { + WRITE_CHANGE, // 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_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 + config_option_t conf; // The configuration option to cange +} command_context_t; + +// Parses the input string and outputs one of the valid commands +command_context_t parse_command(char *command_str); + +// Routes the provided command to the appropriate function to handle it +void route_command(command_context_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* COMMAND_HANDLER_H */ diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index d7f88a4..64209ae 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -4,6 +4,7 @@ + command-handler.h main.c + command-handler.c Date: Wed, 6 Mar 2024 13:51:52 +0100 Subject: [PATCH 2/9] Implement command parser up to second parameter The command parser has been implemented for all options other than the config option. See #1 --- prosjekt.X/command-handler.c | 130 ++++++++++++++++++++++++++++++++++- prosjekt.X/command-handler.h | 18 +++-- 2 files changed, 140 insertions(+), 8 deletions(-) diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c index 91e537c..75d91d1 100644 --- a/prosjekt.X/command-handler.c +++ b/prosjekt.X/command-handler.c @@ -1,5 +1,131 @@ #include "command-handler.h" -command_t parse_command(char *command_str); +command_context_t parse_command(uint8_t *command, uint8_t command_len) { + // Create the context + command_context_t context; -void route_command(command_t command); + /////////////////////// + // Command selection // + /////////////////////// + + // Validate that we have a command + if (command_len < 1) { + context.command = UNKNOWN_COMMAND; + return context; + } + + // Figure out which command to run + switch (command[0]) { + 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 context; + } + + // 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 context; + } + + // 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 context; + } + + // 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 context; + } + + // Store the parameter + param = command[2]; + + // TODO: Handle the config parameters + + // Return the context + return context; +} + +void route_command(command_context_t command); diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h index 7d8c304..d59305a 100644 --- a/prosjekt.X/command-handler.h +++ b/prosjekt.X/command-handler.h @@ -12,9 +12,12 @@ extern "C" { #endif +#include +#include + // Enum of all valid command types typedef enum { - WRITE_CHANGE, // Change the configuration + 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 @@ -26,9 +29,10 @@ typedef enum { // Enum of all valid voltage sources typedef enum { - SRC_INTERNAL, // Internal volage - SRC_EXTRNAL, // External voltage - SRC_NONE // No voltage source selected + 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 @@ -48,12 +52,14 @@ typedef enum { // Struct with command context typedef struct { command_t command; // The command to execute - src_voltage_t src_voltage; // The selected voltage source + 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 -command_context_t parse_command(char *command_str); +command_context_t parse_command(uint8_t *command, uint8_t command_len); // Routes the provided command to the appropriate function to handle it void route_command(command_context_t command); From ac78f33c981ef9f947897f7a48cbba4b29c4901a Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Wed, 6 Mar 2024 14:58:49 +0100 Subject: [PATCH 3/9] 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 --- prosjekt.X/i2c.c | 32 +++++++++++++++++++++++++ prosjekt.X/i2c.h | 26 ++++++++++++++++++++ prosjekt.X/nbproject/configurations.xml | 2 ++ 3 files changed, 60 insertions(+) create mode 100644 prosjekt.X/i2c.c create mode 100644 prosjekt.X/i2c.h diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c new file mode 100644 index 0000000..6474273 --- /dev/null +++ b/prosjekt.X/i2c.c @@ -0,0 +1,32 @@ +#include "i2c.h" + +void init_i2c(void) { + // 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_PIEN_bm // Enable stop flag interrupt + | TWI_SMEN_bm // Enable smart mode + | TWI_ENABLE_bm // Enable acting as a slave + ; +} + +// TODO: Figure out which interrupt does what + +// Interrupt vector +ISR(TWI0_TWIS_vect) { + asm('nop'); +} + +// Interrupt vector +ISR(TWI0_TWIM_vect) { + asm('nop'); +} \ No newline at end of file diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h new file mode 100644 index 0000000..7330fd8 --- /dev/null +++ b/prosjekt.X/i2c.h @@ -0,0 +1,26 @@ +/* + * 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 +#include + +// Initialize the I2C bus +void init_i2c(void); + +#ifdef __cplusplus +} +#endif + +#endif /* I2C_H */ diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index 64209ae..e78921c 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -5,6 +5,7 @@ displayName="Header Files" projectFiles="true"> command-handler.h + i2c.h main.c command-handler.c + i2c.c Date: Wed, 20 Mar 2024 13:00:40 +0100 Subject: [PATCH 4/9] Functioning I2C slave data receive The slave now receives and prints data successfully. --- prosjekt.X/i2c.c | 63 ++++++++++++++++++++++--- prosjekt.X/i2c.h | 2 + prosjekt.X/main.c | 8 +++- prosjekt.X/nbproject/configurations.xml | 3 ++ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c index 6474273..7b0eddc 100644 --- a/prosjekt.X/i2c.c +++ b/prosjekt.X/i2c.c @@ -1,6 +1,15 @@ #include "i2c.h" +// 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 + 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 @@ -13,20 +22,60 @@ void init_i2c(void) { // 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 - | TWI_SMEN_bm // Enable smart mode + | PIN2_bm // Respond to all TWI addresses | TWI_ENABLE_bm // Enable acting as a slave ; } // TODO: Figure out which interrupt does what -// Interrupt vector -ISR(TWI0_TWIS_vect) { - asm('nop'); +void i2c_write_handler(uint8_t data) { + printf("%u\n", data); } -// Interrupt vector -ISR(TWI0_TWIM_vect) { - asm('nop'); +void i2c_read_handler() { + printf("Master wanted to read.\n"); +} + +void i2c_stop_handler() { + printf("Stop, I guess\n"); +} + +// Address received +ISR(TWI0_TWIS_vect) { + // Check for the data interrupt flag + if (TWI0.SSTATUS & TWI_DIF_bm) { + uint8_t data = 0; + + if ( ((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0 ) { + // Data write Master -> Slave + data = TWI0.SDATA; + + // Send the data to the write handler + i2c_write_handler(data); + } else { + // Data read Master <- Slave + i2c_read_handler(); + data = TWI0.SDATA; + } + + // 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; + } + } } \ No newline at end of file diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h index 7330fd8..0e8f64f 100644 --- a/prosjekt.X/i2c.h +++ b/prosjekt.X/i2c.h @@ -15,6 +15,8 @@ extern "C" { // Include the IO for I2C #include #include +#include "uart.h" +#include // Initialize the I2C bus void init_i2c(void); diff --git a/prosjekt.X/main.c b/prosjekt.X/main.c index b42afa9..25f1319 100644 --- a/prosjekt.X/main.c +++ b/prosjekt.X/main.c @@ -10,14 +10,18 @@ #include #include #include "uart.h" +#include "i2c.h" #include -int main() { +int main() { init_uart((uint16_t)9600); + init_i2c(); stdout = &USART_stream; + + sei(); while (1) { - printf("Hello, world!\n"); + printf("\n"); _delay_ms(500); } } \ No newline at end of file diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index 89c9ad4..528acb5 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -171,6 +171,9 @@ + + + From 8e3607cbda45edb0fcc05a482bf4db5edd963387 Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Wed, 20 Mar 2024 13:24:55 +0100 Subject: [PATCH 5/9] Store and hold I2C data I2C data is now stored until stop is sent, then printed. --- prosjekt.X/i2c.c | 24 ++++++++++++++++++++---- prosjekt.X/i2c.h | 6 ++++++ prosjekt.X/uart.h | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c index 7b0eddc..75cdb5f 100644 --- a/prosjekt.X/i2c.c +++ b/prosjekt.X/i2c.c @@ -1,6 +1,9 @@ #include "i2c.h" -// 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 +// 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 + +volatile uint8_t i2c_recv[I2C_RECV_BUF_SIZE]; // Arbitrary length array to hold the received data, longer than max expected command +volatile uint8_t i2c_recv_len; void init_i2c(void) { // Pin setup @@ -27,12 +30,22 @@ void init_i2c(void) { | PIN2_bm // Respond to all TWI addresses | TWI_ENABLE_bm // Enable acting as a slave ; + + // Reset the received stuff + i2c_reset_recv(); } -// TODO: Figure out which interrupt does what +// 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) { - printf("%u\n", data); + i2c_recv[i2c_recv_len] = data; + i2c_recv_len++; } void i2c_read_handler() { @@ -40,7 +53,10 @@ void i2c_read_handler() { } void i2c_stop_handler() { - printf("Stop, I guess\n"); + // Reset counter and clear receive buffer + i2c_recv[i2c_recv_len] = '\0'; + printf("%s\n", i2c_recv); + i2c_reset_recv(); } // Address received diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h index 0e8f64f..6b4214c 100644 --- a/prosjekt.X/i2c.h +++ b/prosjekt.X/i2c.h @@ -18,6 +18,12 @@ extern "C" { #include "uart.h" #include +// 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); diff --git a/prosjekt.X/uart.h b/prosjekt.X/uart.h index 7284552..9ca2621 100644 --- a/prosjekt.X/uart.h +++ b/prosjekt.X/uart.h @@ -20,7 +20,7 @@ extern "C" { #endif #define USART3_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5) - + // Initialize the USART3 controller void init_uart(uint16_t baud); From 9fd877aab525364bb29c7433331c6ea8d82ff3ac Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Tue, 16 Apr 2024 15:30:38 +0200 Subject: [PATCH 6/9] Command handing works! The command handler can now receive a command, then it can run a function / return some data based on it. --- prosjekt.X/command-handler.c | 50 ++++++++--- prosjekt.X/command-handler.h | 5 +- prosjekt.X/i2c.c | 114 ++++++++++++++---------- prosjekt.X/i2c.h | 8 +- prosjekt.X/main.c | 7 +- prosjekt.X/nbproject/configurations.xml | 3 + 6 files changed, 122 insertions(+), 65 deletions(-) diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c index 75d91d1..cb485ba 100644 --- a/prosjekt.X/command-handler.c +++ b/prosjekt.X/command-handler.c @@ -1,9 +1,10 @@ #include "command-handler.h" -command_context_t parse_command(uint8_t *command, uint8_t command_len) { - // Create the context - command_context_t context; +// 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 // /////////////////////// @@ -11,11 +12,12 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) { // Validate that we have a command if (command_len < 1) { context.command = UNKNOWN_COMMAND; - return context; } + uint8_t foo = command[0]; + // Figure out which command to run - switch (command[0]) { + switch (foo) { case 0x11: // Read config context.command = READ_CONFIG; break; @@ -48,14 +50,14 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) { // Check if the command does not require a parameter. If it does not, return. if (context.command == READ_TERMPERATURE) { - return context; + 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 context; + return; } // Store the parameter @@ -109,14 +111,14 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) { // 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 context; + 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 context; + return; } // Store the parameter @@ -124,8 +126,32 @@ command_context_t parse_command(uint8_t *command, uint8_t command_len) { // TODO: Handle the config parameters - // Return the context - return context; + // exit + return; } -void route_command(command_context_t command); +uint8_t route_command() { + 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; + } +} diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h index d59305a..dd87e15 100644 --- a/prosjekt.X/command-handler.h +++ b/prosjekt.X/command-handler.h @@ -12,6 +12,7 @@ extern "C" { #endif +#include #include #include @@ -59,10 +60,10 @@ typedef struct { } command_context_t; // Parses the input string and outputs one of the valid commands -command_context_t parse_command(uint8_t *command, uint8_t command_len); +void parse_command(uint8_t *command, uint8_t command_len); // Routes the provided command to the appropriate function to handle it -void route_command(command_context_t command); +uint8_t route_command(); #ifdef __cplusplus } diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c index 75cdb5f..278f689 100644 --- a/prosjekt.X/i2c.c +++ b/prosjekt.X/i2c.c @@ -1,18 +1,26 @@ #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 +// 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 -volatile uint8_t i2c_recv[I2C_RECV_BUF_SIZE]; // Arbitrary length array to hold the received data, longer than max expected command -volatile uint8_t i2c_recv_len; +// 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 @@ -30,7 +38,7 @@ void init_i2c(void) { | PIN2_bm // Respond to all TWI addresses | TWI_ENABLE_bm // Enable acting as a slave ; - + // Reset the received stuff i2c_reset_recv(); } @@ -39,59 +47,73 @@ void init_i2c(void) { void i2c_reset_recv() { i2c_recv_len = 0; for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) { - i2c_recv[i] = 0; + i2c_recv[i] = 0; } } void i2c_write_handler(uint8_t data) { - i2c_recv[i2c_recv_len] = data; - i2c_recv_len++; + last_action_write = true; + i2c_recv[i2c_recv_len] = data; + i2c_recv_len++; } void i2c_read_handler() { - printf("Master wanted to read.\n"); + last_action_write = false; + TWI0.SDATA = route_command(); } void i2c_stop_handler() { - // Reset counter and clear receive buffer - i2c_recv[i2c_recv_len] = '\0'; - printf("%s\n", i2c_recv); - i2c_reset_recv(); + if (last_action_write) { + // Parse the received command data + parse_command(i2c_recv, i2c_recv_len); + } else { + // route_command(); + ; + } + // Reset the buffer for future transmissions + i2c_reset_recv(); } // Address received ISR(TWI0_TWIS_vect) { - // Check for the data interrupt flag - if (TWI0.SSTATUS & TWI_DIF_bm) { - uint8_t data = 0; - - if ( ((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0 ) { - // Data write Master -> Slave - data = TWI0.SDATA; - - // Send the data to the write handler - i2c_write_handler(data); - } else { - // Data read Master <- Slave - i2c_read_handler(); - data = TWI0.SDATA; - } - - // Acknowledge having received - TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc; + // 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; } - - // 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; - } + + // 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; } -} \ No newline at end of file + } + + // Re-enable interrupts + sei(); +} diff --git a/prosjekt.X/i2c.h b/prosjekt.X/i2c.h index 6b4214c..342f459 100644 --- a/prosjekt.X/i2c.h +++ b/prosjekt.X/i2c.h @@ -13,13 +13,15 @@ extern "C" { #endif // Include the IO for I2C -#include -#include +#include "command-handler.h" #include "uart.h" +#include +#include +#include #include // Received data info -#define I2C_RECV_BUF_SIZE 64 +#define I2C_RECV_BUF_SIZE 64 // Reset recv to initial state void i2c_reset_recv(); diff --git a/prosjekt.X/main.c b/prosjekt.X/main.c index 25f1319..fb95de5 100644 --- a/prosjekt.X/main.c +++ b/prosjekt.X/main.c @@ -11,17 +11,20 @@ #include #include "uart.h" #include "i2c.h" +#include "command-handler.h" #include +#include int main() { init_uart((uint16_t)9600); init_i2c(); stdout = &USART_stream; + PORTB.DIRSET = PIN3_bm; + sei(); while (1) { - printf("\n"); - _delay_ms(500); + ; } } \ No newline at end of file diff --git a/prosjekt.X/nbproject/configurations.xml b/prosjekt.X/nbproject/configurations.xml index 528acb5..e65594c 100644 --- a/prosjekt.X/nbproject/configurations.xml +++ b/prosjekt.X/nbproject/configurations.xml @@ -149,6 +149,9 @@ + + + From 7e59368252120e9c8616342e0ad6b4adcae7872e Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Tue, 16 Apr 2024 15:35:43 +0200 Subject: [PATCH 7/9] Implement handling reading more than one byte --- prosjekt.X/command-handler.c | 2 +- prosjekt.X/command-handler.h | 3 ++- prosjekt.X/i2c.c | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c index cb485ba..2772a4a 100644 --- a/prosjekt.X/command-handler.c +++ b/prosjekt.X/command-handler.c @@ -130,7 +130,7 @@ void parse_command(uint8_t command[], uint8_t command_len) { return; } -uint8_t route_command() { +uint8_t route_command(int pos) { switch (context.command) { WRITE_CONFIG: return WRITE_CONFIG; diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h index dd87e15..696ee2b 100644 --- a/prosjekt.X/command-handler.h +++ b/prosjekt.X/command-handler.h @@ -63,7 +63,8 @@ typedef struct { void parse_command(uint8_t *command, uint8_t command_len); // Routes the provided command to the appropriate function to handle it -uint8_t route_command(); +// If the command is a read command it then returns the current byte +uint8_t route_command(int pos); #ifdef __cplusplus } diff --git a/prosjekt.X/i2c.c b/prosjekt.X/i2c.c index 278f689..c11fdcf 100644 --- a/prosjekt.X/i2c.c +++ b/prosjekt.X/i2c.c @@ -59,16 +59,14 @@ void i2c_write_handler(uint8_t data) { void i2c_read_handler() { last_action_write = false; - TWI0.SDATA = route_command(); + 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); - } else { - // route_command(); - ; } // Reset the buffer for future transmissions i2c_reset_recv(); From b1ca4c05bf49c72377854d2c83f8c4e76f8f020f Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Tue, 16 Apr 2024 16:17:35 +0200 Subject: [PATCH 8/9] Rename foo in command handler to a more descirptive name "foo" -> "command_byte" --- prosjekt.X/command-handler.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/prosjekt.X/command-handler.c b/prosjekt.X/command-handler.c index 2772a4a..e9ade45 100644 --- a/prosjekt.X/command-handler.c +++ b/prosjekt.X/command-handler.c @@ -14,10 +14,11 @@ void parse_command(uint8_t command[], uint8_t command_len) { context.command = UNKNOWN_COMMAND; } - uint8_t foo = command[0]; + // Extract the first byte, which contains the command + uint8_t command_byte = command[0]; // Figure out which command to run - switch (foo) { + switch (command_byte) { case 0x11: // Read config context.command = READ_CONFIG; break; From 5ea7be072804b681de2a0a1eb1e880223dd53b2d Mon Sep 17 00:00:00 2001 From: "Sebastian H. Gabrielli" Date: Tue, 16 Apr 2024 16:20:32 +0200 Subject: [PATCH 9/9] Explain pos variable --- prosjekt.X/command-handler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prosjekt.X/command-handler.h b/prosjekt.X/command-handler.h index 696ee2b..e09afda 100644 --- a/prosjekt.X/command-handler.h +++ b/prosjekt.X/command-handler.h @@ -63,7 +63,8 @@ typedef struct { 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 +// 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