From a0fc82dba9258c8d24a0fa7829989a4e31cd7606 Mon Sep 17 00:00:00 2001 From: Dennis Gunia Date: Sun, 2 Nov 2025 15:18:57 +0100 Subject: [PATCH] combine module firmware revisions into single project. Expand makefile --- .gitignore | 6 +- software/firmware_module/module_rev0/Makefile | 66 ---- .../firmware_module/module_rev0/src/global.h | 58 ---- .../firmware_module/module_rev0/src/main.c | 186 ----------- .../firmware_module/module_rev0/src/mctrl.c | 304 ------------------ .../firmware_module/module_rev0/src/rs458.c | 244 -------------- .../firmware_module/module_rev3/src/mctrl.h | 48 --- .../firmware_module/module_rev3/src/rcount.c | 67 ---- .../firmware_module/module_rev3/src/rcount.h | 21 -- .../firmware_module/module_rev3/src/rs485.h | 41 --- .../Makefile | 51 ++- .../module_universal/build/rev1/src/main.o | Bin 0 -> 15692 bytes .../module_universal/build/rev1/src/mctrl.o | Bin 0 -> 17288 bytes .../module_universal/build/rev1/src/rcount.o | Bin 0 -> 9436 bytes .../module_universal/build/rev1/src/rs458.o | Bin 0 -> 18596 bytes .../module_universal/build/rev2/src/main.o | Bin 0 -> 15692 bytes .../module_universal/build/rev2/src/mctrl.o | Bin 0 -> 17288 bytes .../module_universal/build/rev2/src/rcount.o | Bin 0 -> 9436 bytes .../module_universal/build/rev2/src/rs458.o | Bin 0 -> 18672 bytes .../module_universal/eeprom.hex | 17 + .../src/global.h | 28 +- .../src/main.c | 0 .../src/mctrl.c | 21 +- .../src/mctrl.h | 0 .../src/rcount.c | 0 .../src/rcount.h | 0 .../src/rs458.c | 6 +- .../src/rs485.h | 0 28 files changed, 101 insertions(+), 1063 deletions(-) delete mode 100644 software/firmware_module/module_rev0/Makefile delete mode 100644 software/firmware_module/module_rev0/src/global.h delete mode 100644 software/firmware_module/module_rev0/src/main.c delete mode 100644 software/firmware_module/module_rev0/src/mctrl.c delete mode 100644 software/firmware_module/module_rev0/src/rs458.c delete mode 100644 software/firmware_module/module_rev3/src/mctrl.h delete mode 100644 software/firmware_module/module_rev3/src/rcount.c delete mode 100644 software/firmware_module/module_rev3/src/rcount.h delete mode 100644 software/firmware_module/module_rev3/src/rs485.h rename software/firmware_module/{module_rev3 => module_universal}/Makefile (51%) create mode 100644 software/firmware_module/module_universal/build/rev1/src/main.o create mode 100644 software/firmware_module/module_universal/build/rev1/src/mctrl.o create mode 100644 software/firmware_module/module_universal/build/rev1/src/rcount.o create mode 100644 software/firmware_module/module_universal/build/rev1/src/rs458.o create mode 100644 software/firmware_module/module_universal/build/rev2/src/main.o create mode 100644 software/firmware_module/module_universal/build/rev2/src/mctrl.o create mode 100644 software/firmware_module/module_universal/build/rev2/src/rcount.o create mode 100644 software/firmware_module/module_universal/build/rev2/src/rs458.o create mode 100644 software/firmware_module/module_universal/eeprom.hex rename software/firmware_module/{module_rev3 => module_universal}/src/global.h (83%) rename software/firmware_module/{module_rev3 => module_universal}/src/main.c (100%) rename software/firmware_module/{module_rev3 => module_universal}/src/mctrl.c (95%) rename software/firmware_module/{module_rev0 => module_universal}/src/mctrl.h (100%) rename software/firmware_module/{module_rev0 => module_universal}/src/rcount.c (100%) rename software/firmware_module/{module_rev0 => module_universal}/src/rcount.h (100%) rename software/firmware_module/{module_rev3 => module_universal}/src/rs458.c (96%) rename software/firmware_module/{module_rev0 => module_universal}/src/rs485.h (100%) diff --git a/.gitignore b/.gitignore index 41677d5..f711869 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ -software/firmware_module/module_rev0/build/* -software/firmware_module/module_rev0/build -software/firmware_module/module_rev3/build/* -software/firmware_module/module_rev3/build +software/firmware_module/module_universal/build/* +software/firmware_module/module_universal/build software/pc_client/build/* software/pc_client/build hardware/module_controller/ModuleController-backups/* diff --git a/software/firmware_module/module_rev0/Makefile b/software/firmware_module/module_rev0/Makefile deleted file mode 100644 index 495df94..0000000 --- a/software/firmware_module/module_rev0/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -# Source and include -BUILD_DIR=build -SRCS=$(wildcard src/*.c) -OBJS=$(SRCS:%.c=build/%.o) -INC=-I ./inc - -# MCU configuration -# Set MCU type, clock frequency and programmer -MCU=atmega8 -CLOCK_FREQ=16000000 -PROG_STR=usbasp - -# Compiler flags -CFLAGS=-std=c11 -Wall -Wextra -Werror -mmcu=$(MCU) -DF_CPU=$(CLOCK_FREQ) -OPT_FLAGS=-O3 -g -DDEBUG - -# Compiler and utility tools -OBJCOPY=avr-objcopy -CC=avr-gcc - -# Project configuration -PROJ_NAME=sflap_controller_fw -PROJ_BLD=$(BUILD_DIR)/$(PROJ_NAME) - -TEMP_EEPROM_DUMP=eeprom.hex -# Rules - -all: $(PROJ_BLD).elf - -$(PROJ_BLD).elf: $(OBJS) - $(CC) -o $@ $^ $(INC) $(CFLAGS) $(OPT_FLAGS) $(MCU_FLAGS) - $(OBJCOPY) -j .text -j .data -O ihex $@ $(PROJ_BLD).hex - $(OBJCOPY) -j .text -j .data -O binary $@ $(PROJ_BLD).bin - - -build/%.o: %.c - mkdir -p build/src - $(CC) -c -o $@ $(INC) $(CFLAGS) $(OPT_FLAGS) $(MCU_FLAGS) $< - -release: OPT_FLAGS=-O2 -DNDEBUG -release: $(PROJ_BLD).elf - -fuse: - avrdude -c $(PROG_STR) -p $(MCU) -U lfuse:w:0xDF:m -U hfuse:w:0xCA:m -B 125kHz - -flash-clean: - avrdude -c $(PROG_STR) -p $(MCU) -U flash:w:$(PROJ_BLD).hex:i - -flash-update: - avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:r:$(TEMP_EEPROM_DUMP):i - avrdude -c $(PROG_STR) -p $(MCU) -U flash:w:$(PROJ_BLD).hex:i - avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:w:$(TEMP_EEPROM_DUMP):i - -clear-address: - avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:r:$(TEMP_EEPROM_DUMP):i - sed -i 's/:20000000..../:200000000000/' $(TEMP_EEPROM_DUMP) - avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:w:$(TEMP_EEPROM_DUMP):i - -flash-debug: - avrdude -c $(PROG_STR) -p $(MCU) -U flash:w:$(PROJ_BLD).elf:e - -clean: - rm -rf build - rm $(TEMP_EEPROM_DUMP) - -.PHONY = clean, release, flash, flash-debug \ No newline at end of file diff --git a/software/firmware_module/module_rev0/src/global.h b/software/firmware_module/module_rev0/src/global.h deleted file mode 100644 index 19e38f3..0000000 --- a/software/firmware_module/module_rev0/src/global.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - -#include -#include -#include -#include -#include -#include -#include - - -// I/O Pin definition -#define BUX_RX PD0 // RX Pin (to buffer) -#define BUX_TX PD1 // TX Pin (to buffer) -#define BUX_DIR PD2 // Buffer direction pin -#define SENSOR_HOME PD3 // Home sensor pin - -#define MOTOR_A PC0 // Motor phase A driver -#define MOTOR_B PC1 // Motor phase B driver -#define MOTOR_C PC2 // Motor phase C driver -#define MOTOR_D PC3 // Motor phase D driver - -// EEPROM Addresses -#define CONF_CONST_OKAY (uint8_t)0xAA -#define CONF_ADDR_OKAY 0x0004 -#define CONF_ADDR_ADDR 0x0000 -#define CONF_ADDR_OFFSET 0x0002 - -// Protocol definitions -#define PROTO_MAXPKGLEN 64 // maximum size of package in bytes - -// Command Bytes -#define CMDB_SETVAL (uint8_t)0x10 // Set display value -#define CMDB_SETVALR (uint8_t)0x11 // Set display value and do a full rotation -#define CMDB_EEPROMR (uint8_t)0xF0 // Read EEPROM -#define CMDB_EEPROMW (uint8_t)0xF1 // Write EEPROM -#define CMDB_GSTS (uint8_t)0xF8 // Get status -#define CMDB_PING (uint8_t)0xFE // Ping -#define CMDB_RESET (uint8_t)0x30 // Reset device -#define CMDB_PWRON (uint8_t)0x21 // Power motor on -#define CMDB_RPWROFF (uint8_t)0x20 // Poer motor off - -// Command Responses -#define CMDR_ERR_INVALID 0xEE // Invalid command -#define CMDR_ACK 0xAA // Acknowledge -#define CMDR_PING 0xFF // Ping response - -// Utility definitions -#define SHIFT_0B 0 -#define SHIFT_1B 8 -#define SHIFT_2B 16 -#define SHIFT_3B 24 \ No newline at end of file diff --git a/software/firmware_module/module_rev0/src/main.c b/software/firmware_module/module_rev0/src/main.c deleted file mode 100644 index 87a20f9..0000000 --- a/software/firmware_module/module_rev0/src/main.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - -#include "global.h" -#include "mctrl.h" -#include "rcount.h" -#include "rs485.h" - -uint16_t address = 0x0000; -uint16_t calib_offset = 0x0000; -char *payload; - - -void eeprom_write_c(uint16_t address, uint8_t data) -{ - // disable interrupt - cli(); - while (EECR & (1 << EEWE)) - ; // wait until previous write is done - EEAR = address; - EEDR = data; - EECR |= (1 << EEMWE); // enable Master Write Enable - EECR |= (1 << EEWE); // write one - sei(); -} - -uint8_t eeprom_read_c(uint16_t address) -{ - while (EECR & (1 << EEWE)) - ; // wait until previous write is done - EEAR = address; - EECR |= (1 << EERE); // read one - return EEDR; -} - -void initialSetup() -{ - wdt_disable(); - if (eeprom_read_c(CONF_ADDR_OKAY) == CONF_CONST_OKAY) - { - uint8_t addrL = eeprom_read_c(CONF_ADDR_ADDR); - uint8_t addrH = eeprom_read_c(CONF_ADDR_ADDR + 1); - address = addrL | (addrH << 8); - uint8_t offsetL = eeprom_read_c(CONF_ADDR_OFFSET); - uint8_t offsetH = eeprom_read_c(CONF_ADDR_OFFSET + 1); - calib_offset = (offsetL | (offsetH << 8)); - } - else - { - eeprom_write_c(CONF_ADDR_ADDR, (uint8_t)0x00); - eeprom_write_c(CONF_ADDR_ADDR + 1, (uint8_t)0x00); - eeprom_write_c(CONF_ADDR_OFFSET, (uint8_t)0x00); - eeprom_write_c(CONF_ADDR_OFFSET + 1, (uint8_t)0x00); - eeprom_write_c(CONF_ADDR_OKAY, CONF_CONST_OKAY); - } -} - -void readCommand() -{ - int payload_len = parse_buffer(address, payload); - if (payload_len > 0) // if positive, package is valid - { - payload += 2; // skip address bytes - // read command byte - uint8_t opcode = *payload; - // parse commands - if (opcode == CMDB_SETVAL) - { - // 0x1O = Set Digit - uint8_t targetDigit = *(payload + 1); - mctrl_set(targetDigit, 0); - } - else if (opcode == CMDB_SETVALR) - { - // 0x11 = Set Digit (full rotation) - uint8_t targetDigit = *(payload + 1); - mctrl_set(targetDigit, 1); - } - else if (opcode == CMDB_EEPROMR) - { - // 0xFO = READ EEPROM - uint8_t bytes = 5; - char *msg = malloc(bytes + 1); - *msg = CMDR_ACK; - for (uint16_t i = 1; i < (uint16_t)bytes + 1; i++) - { - *(msg + i) = (char)eeprom_read_c(i - 1); - } - _delay_ms(2); - sfbus_send_frame_v2(0xFFFF, msg, bytes + 1); - free(msg); - } - else if (opcode == CMDB_EEPROMW) - { - // 0xF1 = WRITE EEPROM - eeprom_write_c(CONF_ADDR_OKAY, (char)0xFF); - for (uint16_t i = 0; i < 4; i++) - { - eeprom_write_c(i, *(payload + 1 + i)); - } - eeprom_write_c(CONF_ADDR_OKAY, CONF_CONST_OKAY); - // respond with readout - uint8_t bytes = 5; - char *msg = malloc(bytes + 1); - *msg = CMDR_ACK; - for (uint16_t i = 1; i < (uint16_t)bytes + 1; i++) - { - *(msg + i) = (char)eeprom_read_c(i - 1); - } - _delay_ms(2); - sfbus_send_frame_v2(0xFFFF, msg, bytes + 1); - free(msg); - // now use new addr - uint8_t addrL = eeprom_read_c(CONF_ADDR_ADDR); - uint8_t addrH = eeprom_read_c(CONF_ADDR_ADDR + 1); - address = addrL | (addrH << SHIFT_1B); - } - else if (opcode == CMDB_GSTS) - { - char *msg = malloc(7); - *msg = (char)getSts(); - uint16_t voltage = getVoltage(); - *(msg + 2) = (char)((voltage >> SHIFT_0B) & 0xFF); - *(msg + 1) = (char)((voltage >> SHIFT_1B) & 0xFF); - uint32_t counter = rc_getCounter(); - *(msg + 6) = (char)((counter >> SHIFT_0B) & 0xFF); - *(msg + 5) = (char)((counter >> SHIFT_1B) & 0xFF); - *(msg + 4) = (char)((counter >> SHIFT_2B) & 0xFF); - *(msg + 3) = (char)((counter >> SHIFT_3B) & 0xFF); - _delay_ms(2); - sfbus_send_frame_v2(0xFFFF, msg, 7); - free(msg); - } - else if (opcode == CMDB_PING) - { - char msg = (char)CMDR_PING; - _delay_ms(2); - sfbus_send_frame_v2(0xFFFF, &msg, 1); - } - else if (opcode == CMDB_RPWROFF) - { - mctrl_power(0); - } - else if (opcode == CMDB_PWRON) - { - mctrl_power(1); - } - else if (opcode == CMDB_RESET) - { - do - { - wdt_enable(WDTO_15MS); - for (;;) - { - } - } while (0); - } - else - { - // invalid opcode - char msg = CMDR_ERR_INVALID; - _delay_ms(2); - sfbus_send_frame_v2(0xFFFF, &msg, 1); - } - //memset(payload, 0x00, PROTO_MAXPKGLEN); // clear buffer - } -} - -int main() -{ - payload = malloc(PROTO_MAXPKGLEN); - initialSetup(); - rs485_init(); - setup_async_rx(); - mctrl_init(calib_offset); - - while (1 == 1) - { - readCommand(); - } -} diff --git a/software/firmware_module/module_rev0/src/mctrl.c b/software/firmware_module/module_rev0/src/mctrl.c deleted file mode 100644 index d60559d..0000000 --- a/software/firmware_module/module_rev0/src/mctrl.c +++ /dev/null @@ -1,304 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - - -#include "mctrl.h" - -// Motor driver steps definition. Reverse for direction change. -uint8_t motor_steps[4] = { - 0b00000001, - 0b00000010, - 0b00000100, - 0b00001000, -}; - -uint8_t step_index = 0; // current index in motor_steps -uint8_t target_flap = 0; // target flap -uint16_t absolute_pos = 0; // absolute position in steps -uint16_t steps_since_home = 0; // steps since last home signal - -// homing util variables -uint8_t homing = 0; // current homing step -uint8_t lastSens = 0; // home sonsor signal from last tick - -// counter for auto powersaving -uint8_t ticksSinceMove = 0; - -// value to goto after the current is reached. 255 = NONE. -uint8_t afterRotation = STEPS_AFTERROT; - -int16_t *delta_err; - -// error and status flags -uint8_t sts_flag_errorTooBig = 0; // last home signal too early or too late -uint8_t sts_flag_noHome = 0; // no home signal detected. Wheel stuck -uint8_t sts_flag_fuse = 0; // blown fuse detcted -uint8_t sts_flag_pwrdwn = 0; // device is powered down by controller -uint8_t sts_flag_failsafe = 0; // device is powered down for safety reasons -uint8_t sts_flag_busy = 0; // device is busy -// voltage monitoring variables -uint16_t currentVoltage = 0; // current ADC reading -uint8_t currentFaultReadings = 0; // ticks with faulty readings (too many will - // trip pwrdwn and sts_flag_fuse) -uint16_t timer_ticks = 0; - -int STEPS_OFFSET = 0; -// initialize motor controller -void mctrl_init(int cal_offset) -{ - if (cal_offset < 800){ - STEPS_OFFSET = STEPS_OFFSET_DEF; - }else{ - STEPS_OFFSET = cal_offset; - } - DDRC = 0x0F; // set all pins as outputs - PORTC = 0x00; // set all to LOW - - DDRD &= ~(1 << PD3); // PD3 is input - PORTD |= (1 << PD3); // PD3 pullup - - // setup adc - ADMUX = 0x07; // Aref, ADC7 - ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADPS1); // Enable ADC, Start first - // reading No freerunning, 8MHz - while ((ADCSRA & (1 << ADSC)) > 0) - { - }; - // wait until first reading is complete, - // to avoid error flag on first tick! - - // setup timer for ISR - TCCR1A = 0; - TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // CTC und Prescaler 64 - OCR1A = MISR_OCR1A; - TIMSK = 1 << OCIE1A; // Timerinterrupts aktivieren - homing = 1; - delta_err = malloc(ERROR_DATASETS * sizeof(uint16_t)); - _delay_ms(MDELAY_STARTUP); - sei(); -} - -// call when critical fail. Powers down motor and sets flags -void failSafe() -{ - sts_flag_failsafe = 1; - PORTC = 0x00; -} - -// read voltage non blocking (called every tick) -void readVoltage() -{ - currentVoltage = ADC; // read last measurement - ADMUX = 0x07; // select ADC7 - ADCSRA |= (1 << ADSC); // trigger next reading - if (currentVoltage < MVOLTAGE_LSTOP) - { // if voltage is too low, fuse is probably broken - currentFaultReadings++; - if (currentFaultReadings > MVOLTAGE_FAULTRD) - { // too many fault readings trigger failSafe - sts_flag_fuse = 1; - failSafe(); - } - } -} - -// MAIN service routine. Called by timer 1 -ISR(TIMER1_COMPA_vect) -{ - timer_ticks ++; - readVoltage(); // read and check voltage - if (sts_flag_pwrdwn == 1 || sts_flag_failsafe == 1) - { - return; - } // if sts_flag_pwrdwn, STOP! - if (steps_since_home > STEPS_PER_REV * MHOME_TOLERANCE) - { // check if home is missing for too long - // home missing error. Wheel probably stuck or power fail - sts_flag_noHome = 1; - failSafe(); - } - else if (homing == 1) - { // Homing procedure 1. step: move out of home - if ((PIND & (1 << PD3)) > 0) - { - homing = 2; - } - else - { - mctrl_step(); - } - } - else if (homing == 2) - { // Homing procedure 2. step: find magnet - mctrl_step(); - if ((PIND & (1 << PD3)) == 0) - { - homing = 3; - steps_since_home = 0; - absolute_pos = STEPS_OFFSET; - incrementCounter(); - } - } - else if (homing == 3) - { // Homing procedure 3. step: apply offset - if (absolute_pos <= 0) - { - homing = 0; - absolute_pos = STEPS_OFFSET; // set correct position again - } - mctrl_step(); - absolute_pos--; - } - else - { // when no failsafe is triggered and homing is done - // calculate target position - uint16_t target_pos = (target_flap * STEPS_PER_FLAP) + STEPS_OFFSET; - if (target_pos >= STEPS_PER_REV) - { - target_pos -= STEPS_PER_REV; - } - if (absolute_pos != target_pos) - { - // if target position is not reached, move motor - ticksSinceMove = 0; - mctrl_step(); - absolute_pos++; - if (absolute_pos >= STEPS_PER_REV) - { - absolute_pos -= STEPS_PER_REV; - } - // detect home position - if ((PIND & (1 << PD3)) == 0) - { - if (lastSens == 0) - { - // new home transition - int16_t errorDelta = - (int16_t)(absolute_pos > (STEPS_PER_REV / 2) ? absolute_pos - STEPS_PER_REV : absolute_pos); - sts_flag_errorTooBig = (errorDelta > MHOME_ERRDELTA) || (errorDelta < -MHOME_ERRDELTA) ? 1 : 0; - // storeErr(errorDelta); - absolute_pos = 0; - steps_since_home = 0; - // increment rotations counter - incrementCounter(); - } - lastSens = 1; - } - else - { - lastSens = 0; - } - } - else - { // if target position is reached - if (afterRotation < (AMOUNTFLAPS + 5)) - { // if after rotation is set, apply it as new target - target_flap = afterRotation; - afterRotation = STEPS_AFTERROT; - } - else if (ticksSinceMove < 2) - { // if motor has not been moved - sts_flag_busy = 0; - } - else if (ticksSinceMove < MPWRSVG_TICKSTOP) - { // if motor has not been moved - ticksSinceMove++; - } - else - { // power off after 50 ticks - // PORTC = 0x00; // turn off stepper - } - } - } - rc_tick(); // process counter tick, non-blocking -} - -// TODO -void storeErr(int16_t error) -{ - int16_t *delta_err_tmp = malloc(ERROR_DATASETS * sizeof(uint16_t)); - memcpy(delta_err, delta_err_tmp + sizeof(uint16_t), ((ERROR_DATASETS - 2) * sizeof(uint16_t))); - memcpy(&error, delta_err_tmp, sizeof(uint16_t)); - free(delta_err); - delta_err = delta_err_tmp; -} -// TODO -void getErr(int16_t *error) -{ - memcpy(delta_err, error, (ERROR_DATASETS * sizeof(uint16_t))); -} - -// return status flag -uint8_t getSts() -{ - uint8_t status = sts_flag_errorTooBig; // bit 0: delta too big - status |= sts_flag_noHome << 1; // bit 1: no home found - status |= sts_flag_fuse << 2; // bit 2: fuse blown - status |= sts_flag_pwrdwn << 4; // bit 4: device powered down - status |= sts_flag_failsafe << 5; // bit 5: failsafe active - status |= sts_flag_busy << 6; // bit 6: device busy - if ((PIND & (1 << PD3)) == 0) - { - status |= (1 << 3); - } - return status; -} - -// return voltage -uint16_t getVoltage() -{ - return currentVoltage; -} - -// set target flap -void mctrl_set(uint8_t flap, uint8_t fullRotation) -{ - sts_flag_busy = 1; - if (fullRotation == 0) - { - target_flap = flap; - } - else - { - target_flap = (target_flap + (STEPS_PER_FLAP - 1)) % STEPS_PER_FLAP; - afterRotation = flap; - } -} - -// trigger home procedure -void mctrl_home() -{ - homing = 1; -} - -// change motor power state -void mctrl_power(uint8_t state) -{ - if (state == 0) - { - sts_flag_pwrdwn = 1; - PORTC = 0x00; - } - else - { - sts_flag_pwrdwn = 0; - PORTC = motor_steps[step_index]; - } -} - -// do stepper step (I/O) -void mctrl_step() -{ - step_index++; - steps_since_home++; - if (step_index > 3) - { - step_index = 0; - } - PORTC = motor_steps[step_index]; -} diff --git a/software/firmware_module/module_rev0/src/rs458.c b/software/firmware_module/module_rev0/src/rs458.c deleted file mode 100644 index b97478c..0000000 --- a/software/firmware_module/module_rev0/src/rs458.c +++ /dev/null @@ -1,244 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - -#include "mctrl.h" -#include "rs485.h" - -void rs485_init() -{ - // init I/O - DDRD &= ~(1 << PD0); // BUS_DIR & TX is OUTPUT - DDRD |= (1 << PD2) | (1 << PD1); // BUS_DIR & TX is OUTPUT - PORTD &= 0x07; // clear PD0-PD4 - // init UART - UBRRH = (BAUDRATE >> 8); - UBRRL = BAUDRATE; // set baud rate - UCSRB |= (1 << TXEN) | (1 << RXEN); // enable receiver and transmitter - UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // 8bit data format -} - -// send byte over rs485 -void rs485_send_c(char data) -{ - PORTD |= (1 << PD2); // set transciever to transmitt - while (!(UCSRA & (1 << UDRE))) - ; // wait until buffer is empty - UCSRA = (1 << TXC); // clear transmit Complete bit - UDR = data; - while (!(UCSRA & (1 << TXC))) - { - }; // wait until transmitt complete - PORTD &= ~(1 << PD2); // set transciever back to receive -} - -// receive without timeout -char rs485_recv_c() -{ - while (!(UCSRA & (1 << RXC))) - ; // wait while data is being received - return UDR; -} - -// receive with timeout -int rs485_recv_c_rxout(uint8_t timeout, char *data) -{ - timer_ticks = 0; - while (!(UCSRA & (1 << RXC))) - { - if (timer_ticks > timeout) - { - return 0; - } - } - *data = UDR; - return 1; -} - -// SFBUS Functions -// receive paket with crc checking and timeout -int sfbus_recv_frame_v2(uint16_t address, char *payload) -{ - const uint8_t RX_TIMEOUT = 4; - while (rs485_recv_c() != SFBUS_SOF_BYTE) - { - } // Wait for start byte - uint8_t frm_version, frm_length; - if (rs485_recv_c_rxout(RX_TIMEOUT, (char *)&frm_version) == 0) // read header: version - return -1; - if (rs485_recv_c_rxout(RX_TIMEOUT, (char *)&frm_length) == 0) // read header: payload length - return -1; - - // ALWAYS!!! receive full packet to avoid sync error - for (int i = 0; i < frm_length; i++) - { - if (rs485_recv_c_rxout(RX_TIMEOUT, payload + i) == 0) - { - return -1; - } - } - // check protocol version - if (frm_version != 0x01) - { - return -1; // abort on incompatible version - } - // if version matches, read address and checksum - uint16_t recv_addr = (*(payload + 0) & 0xFF) | ((*(payload + 1) & 0xFF) << SHIFT_1B); - uint16_t checksum = (*(payload + (frm_length - 2)) & 0xFF) | ((*(payload + (frm_length - 1)) & 0xFF) << SHIFT_1B); - - // calculate checksum of received payload - uint16_t checksum_calc = calc_CRC16(payload + 2, frm_length - 4); - - // return length on valid address and crc, return 0 to ignore - return (checksum == checksum_calc && address == recv_addr) ? frm_length : 0; -} - -// send paket with crc -void sfbus_send_frame_v2(uint16_t address, char *payload, uint8_t length) -{ - uint8_t framelen = length; - // claculate crc value - uint16_t crc = calc_CRC16(payload, framelen); - - rs485_send_c(SFBUS_SOF_BYTE); // send startbyte - rs485_send_c(1); // send protocol version - rs485_send_c((char)(framelen + 4)); // send lentgh of remaining frame - - rs485_send_c((char)(address & 0xFF)); // target address - rs485_send_c((char)((address >> SHIFT_1B) & 0xFF)); - - while (framelen > 0) - { // send payload - rs485_send_c(*payload); - payload++; - framelen--; - } - - rs485_send_c((char)(crc & 0xFF)); // send crc - rs485_send_c((char)((crc >> SHIFT_1B) & 0xFF)); -} - -// calculate crc checksum -uint16_t calc_CRC16(char *buffer, uint8_t len) -{ - uint16_t crc16 = 0xFFFF; - for (uint8_t pos = 0; pos < len; pos++) - { - char byte_value = *(buffer); // read byte after byte from buffer - buffer++; - - crc16 ^= byte_value; // XOR byte into least sig. byte of crc - for (int i = 8; i != 0; i--) - { // Loop over each bit - if ((crc16 & 0x0001) != 0) - { // If the LSB is set - crc16 >>= 1; // Shift right and XOR 0xA001 - crc16 ^= 0xA001; - } - else - { // Else LSB is not set - crc16 >>= 1; // Just shift right - } - } - } - return crc16; -} - -// async receive function -uint8_t rx_buffer_ix_ptr = 0; // receive buffer index pointer -uint8_t rx_buffer_ix_rem = 255; // receive buffer remaining -uint8_t rx_buffer_rx_done = 0; // receive buffer complete flag (must be reset before next pkg can be received) -uint8_t *rx_buffer_temp; // receive buffer - -// setup async receive function -void setup_async_rx() -{ - rx_buffer_temp = malloc(PROTO_MAXPKGLEN); // reserve memory for rx buffer - UCSRB |= (1 << RXCIE); //enable rx interrupt -} - -ISR(USART_RXC_vect) -{ - if (rx_buffer_rx_done == 0) // only receive when last buffer was processed - { - uint8_t rx_byte = UDR; // read rx buffer - if (timer_ticks > 6) // too long since last byte - { - rx_buffer_ix_ptr = 0; // reset pointer and start new transaction - rx_buffer_ix_rem = 255; // reset remaining bytes to default - } - if (rx_buffer_ix_ptr == 0) - { - // wait for startbyte - if (rx_byte == SFBUS_SOF_BYTE) - { - *(rx_buffer_temp) = rx_byte; - rx_buffer_ix_ptr++; - } - } - else if (rx_buffer_ix_ptr > 0 && rx_buffer_ix_ptr < (PROTO_MAXPKGLEN - 1)) // when package is already started - { // and buffer is smaller than max length - *(rx_buffer_temp + rx_buffer_ix_ptr) = rx_byte; // receive single byte - rx_buffer_ix_rem--; - rx_buffer_ix_ptr++; - if (rx_buffer_ix_ptr == 3) // third byte is always frame length - { - rx_buffer_ix_rem = rx_byte; - } - if (rx_buffer_ix_rem == 0) // when last byte is received - { - rx_buffer_ix_ptr--; - rx_buffer_rx_done = 1; - } - } - timer_ticks = 0; // reset timer ticks - } -} - -int parse_buffer(uint16_t address, char *payload) -{ - if (rx_buffer_rx_done > 0) - { - uint8_t frm_length = *(rx_buffer_temp + 2); - // check protocol version - if (*(rx_buffer_temp + 1) != 0x01) - { - clear_buffer(); - return -1; // abort on incompatible version - } - if (frm_length < PROTO_MAXPKGLEN) // prevent buffer overflow - { - memcpy(payload, rx_buffer_temp + 3, frm_length); // copy buffer - // if version matches, read address and checksum - uint16_t recv_addr = (*(rx_buffer_temp + 3) & 0xFF) | ((*(rx_buffer_temp + 4) & 0xFF) << SHIFT_1B); - uint16_t checksum = (*(rx_buffer_temp + (rx_buffer_ix_ptr - 1)) & 0xFF) | - ((*(rx_buffer_temp + (rx_buffer_ix_ptr)) & 0xFF) << SHIFT_1B); - // calculate checksum of received payload - uint16_t checksum_calc = calc_CRC16((char *)rx_buffer_temp + 5, rx_buffer_ix_ptr - 6); - - //mctrl_set(address == recv_addr ? 20 : recv_addr + 1, 0); - clear_buffer(); - // return length on valid address and crc, return 0 to ignore - return (checksum == checksum_calc && address == recv_addr) ? frm_length : 0; - } - else - { - // invalid package - clear_buffer(); - return -1; - } - } - return -2; -} - -void clear_buffer() -{ - rx_buffer_ix_ptr = 0; // reset pointer and start new transaction - rx_buffer_ix_rem = PROTO_MAXPKGLEN; // reset remaining bytes to default - rx_buffer_rx_done = 0; // reset done flag - //memset(rx_buffer_temp, 0x00, PROTO_MAXPKGLEN); // clear buffer -} \ No newline at end of file diff --git a/software/firmware_module/module_rev3/src/mctrl.h b/software/firmware_module/module_rev3/src/mctrl.h deleted file mode 100644 index 1277e41..0000000 --- a/software/firmware_module/module_rev3/src/mctrl.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - - -#include "global.h" -#include "rcount.h" - -#pragma once - -#define STEPS_PER_REV 2025 // steps per revolution -#define STEPS_PER_FLAP 45 // steps per flap -#define STEPS_ADJ 0 // added per flap to compensate for motor power down -#define STEPS_OFFSET_DEF 1400 // ansolute offset between home and first flap -#define AMOUNTFLAPS 45 // amount of flaps installed in system -#define STEPS_AFTERROT 255 // value to goto after current target flap is reached -#define ERROR_DATASETS 8 // length of error array - -#define MDELAY_STARTUP 1000 // delay to wait after motor startup -#define MHOME_TOLERANCE 1.5 // tolerance for intial homing procedure -#define MHOME_ERRDELTA 30 // maximum deviation between expected home and actual home -#define MVOLTAGE_FAULTRD 20 // max. amount of fault readings before flag is set -#define MVOLTAGE_LSTOP 128 // lower voltage threshold for fuse detection -#define MPWRSVG_TICKSTOP 50 // inactive ticks before motor shutdown - -#define MISR_OCR1A 580 // tick timer (defines rotation speed) -// 450, 480 also possible ? - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -void mctrl_init(int cal_offset); -void mctrl_step(); -void mctrl_set(uint8_t flap, uint8_t fullRotation); - -void getErr(int16_t* error); -uint8_t getSts(); -uint16_t getVoltage(); -void mctrl_power(uint8_t state); - -uint16_t timer_ticks; -#ifdef __cplusplus -} -#endif // __cplusplus diff --git a/software/firmware_module/module_rev3/src/rcount.c b/software/firmware_module/module_rev3/src/rcount.c deleted file mode 100644 index 1975456..0000000 --- a/software/firmware_module/module_rev3/src/rcount.c +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - - -#include "rcount.h" - -uint8_t rc_eeprom_write_c(uint16_t address, uint8_t data) -{ - // disable interrupt - if (EECR & (1 << EEWE)) - { - return 1; - } - EEAR = address; - EEDR = data; - EECR |= (1 << EEMWE); // enable Master Write Enable - EECR |= (1 << EEWE); // write one - return 0; -} - -uint8_t rc_eeprom_read_c(uint16_t address) -{ - while (EECR & (1 << EEWE)) - ; // wait until previous write is done - EEAR = address; - EECR |= (1 << EERE); // read one - return EEDR; -} - -uint32_t counter = (uint32_t)0xFFFFFFFF; -uint8_t counter_phase = 5; -void rc_tick() -{ - if (counter == (uint32_t)0xFFFFFFFF) - { - counter = rc_getCounter(); - } - if (counter_phase < 5) - { - cli(); - if (rc_eeprom_write_c(0x100 + counter_phase, ((counter >> (counter_phase * 8)) & 0xFF)) == 0) - { - counter_phase++; - } - sei(); - } -} - -void incrementCounter() -{ - counter++; - counter_phase = 0; -} - -uint32_t rc_getCounter() -{ - uint32_t counter = rc_eeprom_read_c(RC_BASEADDR); - counter |= ((uint32_t)rc_eeprom_read_c(RC_BASEADDR + 1) << SHIFT_1B); - counter |= ((uint32_t)rc_eeprom_read_c(RC_BASEADDR + 2) << SHIFT_2B); - counter |= ((uint32_t)rc_eeprom_read_c(RC_BASEADDR + 3) << SHIFT_3B); - return counter; -} diff --git a/software/firmware_module/module_rev3/src/rcount.h b/software/firmware_module/module_rev3/src/rcount.h deleted file mode 100644 index 6b848a0..0000000 --- a/software/firmware_module/module_rev3/src/rcount.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ - -#include "global.h" - -#define RC_BASEADDR 0x100 - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -void incrementCounter(); -uint32_t rc_getCounter(); -void rc_tick(); -#ifdef __cplusplus -} -#endif // __cplusplus diff --git a/software/firmware_module/module_rev3/src/rs485.h b/software/firmware_module/module_rev3/src/rs485.h deleted file mode 100644 index c162815..0000000 --- a/software/firmware_module/module_rev3/src/rs485.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2025 Dennis Gunia - All Rights Reserved - * You may use, distribute and modify this code under the - * terms of the AGPL-3.0 license. - * - * https://www.dennisgunia.de - * https://github.com/dennis9819/splitflap_v1 - */ -#include "global.h" - -#pragma once -//#define F_CPU 16000000UL -//#define UART_BAUD 19200 // RS485 baud rate -#define UART_BAUD 57600 // RS485 baud rate -#define BAUDRATE ((F_CPU) / (UART_BAUD * 16UL) - 1) // set baud rate value for UBRR - -#define SFBUS_SOF_BYTE '+' // Byte marks start of frame -#define SFBUS_EOF_BYTE '$' // Byte marks end of frame - -#ifdef __cplusplus -extern "C" -{ -#endif // __cplusplus - - void rs485_init(void); - void rs485_send_c(char data); - char rs485_recv_c(void); - int rs485_recv_c_rxout(uint8_t timeout, char *data); - - int sfbus_recv_frame_v2(uint16_t address, char *payload); - void sfbus_send_frame_v2(uint16_t address, char *payload, uint8_t length); - uint16_t calc_CRC16(char *buffer, uint8_t len); - - void setup_async_rx(); - int parse_buffer(uint16_t address, char *payload); - void clear_buffer(); - // auxilary var for uart timeout - extern uint16_t timer_ticks; - -#ifdef __cplusplus -} -#endif // __cplusplus diff --git a/software/firmware_module/module_rev3/Makefile b/software/firmware_module/module_universal/Makefile similarity index 51% rename from software/firmware_module/module_rev3/Makefile rename to software/firmware_module/module_universal/Makefile index cb46db2..9793181 100644 --- a/software/firmware_module/module_rev3/Makefile +++ b/software/firmware_module/module_universal/Makefile @@ -1,7 +1,8 @@ # Source and include BUILD_DIR=build SRCS=$(wildcard src/*.c) -OBJS=$(SRCS:%.c=build/%.o) +OBJS_REV1=$(SRCS:%.c=build/rev1/%.o) +OBJS_REV2=$(SRCS:%.c=build/rev2/%.o) INC=-I ./inc # MCU configuration @@ -10,9 +11,12 @@ MCU=atmega8 CLOCK_FREQ=16000000 PROG_STR=usbasp +BOARD_REVISION=1 + # Compiler flags CFLAGS=-std=c11 -Wall -Wextra -Werror -mmcu=$(MCU) -DF_CPU=$(CLOCK_FREQ) -OPT_FLAGS=-O3 -g -DDEBUG +OPT_FLAGS_REV1=-O3 -g -DDEBUG -DBOARD_REV=1 +OPT_FLAGS_REV2=-O3 -g -DDEBUG -DBOARD_REV=2 # Compiler and utility tools OBJCOPY=avr-objcopy @@ -20,31 +24,52 @@ CC=avr-gcc # Project configuration PROJ_NAME=sflap_controller_fw -PROJ_BLD=$(BUILD_DIR)/$(PROJ_NAME) +PROJ_BLD_REV1=$(BUILD_DIR)/$(PROJ_NAME)_rev1 +PROJ_BLD_REV2=$(BUILD_DIR)/$(PROJ_NAME)_rev2 TEMP_EEPROM_DUMP=eeprom.hex + + +ifeq ($(REV),1) + PROJ_BLD=$(PROJ_BLD_REV1) +else + ifeq ($(REV),2) + PROJ_BLD=$(PROJ_BLD_REV2) + endif +endif + # Rules -all: $(PROJ_BLD).elf +# Build firmware for both revisions +all: $(PROJ_BLD_REV1).elf $(PROJ_BLD_REV2).elf -$(PROJ_BLD).elf: $(OBJS) - $(CC) -o $@ $^ $(INC) $(CFLAGS) $(OPT_FLAGS) $(MCU_FLAGS) - $(OBJCOPY) -j .text -j .data -O ihex $@ $(PROJ_BLD).hex - $(OBJCOPY) -j .text -j .data -O binary $@ $(PROJ_BLD).bin +$(PROJ_BLD_REV1).elf: $(OBJS_REV1) + $(CC) -o $@ $^ $(INC) $(CFLAGS) $(OPT_FLAGS_REV1) $(MCU_FLAGS) + $(OBJCOPY) -j .text -j .data -O ihex $@ $(PROJ_BLD_REV1).hex + $(OBJCOPY) -j .text -j .data -O binary $@ $(PROJ_BLD_REV1).bin + +$(PROJ_BLD_REV2).elf: $(OBJS_REV2) + $(CC) -o $@ $^ $(INC) $(CFLAGS) $(OPT_FLAGS_REV2) $(MCU_FLAGS) + $(OBJCOPY) -j .text -j .data -O ihex $@ $(PROJ_BLD_REV2).hex + $(OBJCOPY) -j .text -j .data -O binary $@ $(PROJ_BLD_REV2).bin + +build/rev1/%.o: %.c + mkdir -p build/rev1/src + $(CC) -c -o $@ $(INC) $(CFLAGS) $(OPT_FLAGS_REV1) $(MCU_FLAGS) $< +build/rev2/%.o: %.c + mkdir -p build/rev2/src + $(CC) -c -o $@ $(INC) $(CFLAGS) $(OPT_FLAGS_REV2) $(MCU_FLAGS) $< -build/%.o: %.c - mkdir -p build/src - $(CC) -c -o $@ $(INC) $(CFLAGS) $(OPT_FLAGS) $(MCU_FLAGS) $< release: OPT_FLAGS=-O2 -DNDEBUG -release: $(PROJ_BLD).elf +release: $(PROJ_BLD_REV1).elf $(PROJ_BLD_REV2).elf fuse: avrdude -c $(PROG_STR) -p $(MCU) -U lfuse:w:0xDF:m -U hfuse:w:0xCA:m -B 125kHz flash-clean: - avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:r:$(TEMP_EEPROM_DUMP):i + #avrdude -c $(PROG_STR) -p $(MCU) -U eeprom:r:$(TEMP_EEPROM_DUMP):i avrdude -c $(PROG_STR) -p $(MCU) -U flash:w:$(PROJ_BLD).hex:i flash-update: diff --git a/software/firmware_module/module_universal/build/rev1/src/main.o b/software/firmware_module/module_universal/build/rev1/src/main.o new file mode 100644 index 0000000000000000000000000000000000000000..9ba2f19e013c03b412914d05ddcc660ed772f1ad GIT binary patch literal 15692 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 literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/build/rev1/src/mctrl.o b/software/firmware_module/module_universal/build/rev1/src/mctrl.o new file mode 100644 index 0000000000000000000000000000000000000000..53582c18d224f75554357377b94a687182b327e6 GIT binary patch literal 17288 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 N$0VgnoQkx=^uLw`56%Dp literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/build/rev1/src/rcount.o b/software/firmware_module/module_universal/build/rev1/src/rcount.o new file mode 100644 index 0000000000000000000000000000000000000000..972ce8ca00767e5f3a1bc9883e4b513c6e736e19 GIT binary patch literal 9436 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 literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/build/rev1/src/rs458.o b/software/firmware_module/module_universal/build/rev1/src/rs458.o new file mode 100644 index 0000000000000000000000000000000000000000..20e4ec4d854c071758effa16cd95a8f2073cd5d2 GIT binary patch literal 18596 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 literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/build/rev2/src/rcount.o b/software/firmware_module/module_universal/build/rev2/src/rcount.o new file mode 100644 index 0000000000000000000000000000000000000000..972ce8ca00767e5f3a1bc9883e4b513c6e736e19 GIT binary patch literal 9436 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 literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/build/rev2/src/rs458.o b/software/firmware_module/module_universal/build/rev2/src/rs458.o new file mode 100644 index 0000000000000000000000000000000000000000..c626b729e2199a60baf28c43f076df09cb5581d0 GIT binary patch literal 18672 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 o+papaW$%9r{Ktsij=+o0f?TfjsO4v literal 0 HcmV?d00001 diff --git a/software/firmware_module/module_universal/eeprom.hex b/software/firmware_module/module_universal/eeprom.hex new file mode 100644 index 0000000..3fbf1b0 --- /dev/null +++ b/software/firmware_module/module_universal/eeprom.hex @@ -0,0 +1,17 @@ +:2000000001006405AAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7 +:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 +:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0 +:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0 +:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80 +:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60 +:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40 +:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20 +:200100002500000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5 +:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF +:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF +:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F +:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F +:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F +:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F +:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F +:00000001FF diff --git a/software/firmware_module/module_rev3/src/global.h b/software/firmware_module/module_universal/src/global.h similarity index 83% rename from software/firmware_module/module_rev3/src/global.h rename to software/firmware_module/module_universal/src/global.h index f1e4160..e8e865f 100644 --- a/software/firmware_module/module_rev3/src/global.h +++ b/software/firmware_module/module_universal/src/global.h @@ -59,6 +59,30 @@ #define SHIFT_3B 24 // Firmware Version -#define FVER_SEMVER_MAJOR 2 #define FVER_SEMVER_MINOR 0 -#define FVER_SEMVER_PATCH 0 \ No newline at end of file +#define FVER_SEMVER_PATCH 0 + +// Hardware Config +#if BOARD_REV == 1 + +#define PIN_SENSE PD3 +#define PIN_RS485_TXE PD2 +#define PIN_RS485_RXE PD2 +#define FVER_SEMVER_MAJOR 1 +#define MOTOR_DIR 0 + + +#elif BOARD_REV == 2 + +#define PIN_SENSE PD4 +#define PIN_RS485_TXE PD2 +#define PIN_RS485_RXE PD3 +#define FVER_SEMVER_MAJOR 2 +#define MOTOR_DIR 1 + +#else + +#define FVER_SEMVER_MAJOR 0 +#error "BOARD_REV not specified!" + +#endif \ No newline at end of file diff --git a/software/firmware_module/module_rev3/src/main.c b/software/firmware_module/module_universal/src/main.c similarity index 100% rename from software/firmware_module/module_rev3/src/main.c rename to software/firmware_module/module_universal/src/main.c diff --git a/software/firmware_module/module_rev3/src/mctrl.c b/software/firmware_module/module_universal/src/mctrl.c similarity index 95% rename from software/firmware_module/module_rev3/src/mctrl.c rename to software/firmware_module/module_universal/src/mctrl.c index 90286bb..359f4dd 100644 --- a/software/firmware_module/module_rev3/src/mctrl.c +++ b/software/firmware_module/module_universal/src/mctrl.c @@ -10,12 +10,21 @@ #include "mctrl.h" // Motor driver steps definition. Reverse for direction change. +#if MOTOR_DIR == 1 uint8_t motor_steps[4] = { 0b00001000, 0b00000100, 0b00000010, 0b00000001, }; +#elif MOTOR_DIR == 0 +uint8_t motor_steps[4] = { + 0b00000001, + 0b00000010, + 0b00000100, + 0b00001000, +}; +#endif uint8_t step_index = 0; // current index in motor_steps uint8_t target_flap = 0; // target flap @@ -59,8 +68,8 @@ void mctrl_init(int cal_offset) DDRC = 0x0F; // set all pins as outputs PORTC = 0x00; // set all to LOW - DDRD &= ~(1 << PD4); // PD4 is input - PORTD |= (1 << PD4); // PD4 pullup + DDRD &= ~(1 << PIN_SENSE); // PIN_SENSE is input + PORTD |= (1 << PIN_SENSE); // PIN_SENSE pullup // setup adc ADMUX = 0xC7; // Internal Vref at 2.56V, ADC7 @@ -124,7 +133,7 @@ ISR(TIMER1_COMPA_vect) } else if (homing == 1) { // Homing procedure 1. step: move out of home - if ((PIND & (1 << PD4)) > 0) + if ((PIND & (1 << PIN_SENSE)) > 0) { homing = 2; } @@ -136,7 +145,7 @@ ISR(TIMER1_COMPA_vect) else if (homing == 2) { // Homing procedure 2. step: find magnet mctrl_step(); - if ((PIND & (1 << PD4)) == 0) + if ((PIND & (1 << PIN_SENSE)) == 0) { homing = 3; steps_since_home = 0; @@ -173,7 +182,7 @@ ISR(TIMER1_COMPA_vect) absolute_pos -= STEPS_PER_REV; } // detect home position - if ((PIND & (1 << PD4)) == 0) + if ((PIND & (1 << PIN_SENSE)) == 0) { if (lastSens == 0) { @@ -242,7 +251,7 @@ uint8_t getSts() status |= sts_flag_pwrdwn << 4; // bit 4: device powered down status |= sts_flag_failsafe << 5; // bit 5: failsafe active status |= sts_flag_busy << 6; // bit 6: device busy - if ((PIND & (1 << PD4)) == 0) + if ((PIND & (1 << PIN_SENSE)) == 0) { status |= (1 << 3); } diff --git a/software/firmware_module/module_rev0/src/mctrl.h b/software/firmware_module/module_universal/src/mctrl.h similarity index 100% rename from software/firmware_module/module_rev0/src/mctrl.h rename to software/firmware_module/module_universal/src/mctrl.h diff --git a/software/firmware_module/module_rev0/src/rcount.c b/software/firmware_module/module_universal/src/rcount.c similarity index 100% rename from software/firmware_module/module_rev0/src/rcount.c rename to software/firmware_module/module_universal/src/rcount.c diff --git a/software/firmware_module/module_rev0/src/rcount.h b/software/firmware_module/module_universal/src/rcount.h similarity index 100% rename from software/firmware_module/module_rev0/src/rcount.h rename to software/firmware_module/module_universal/src/rcount.h diff --git a/software/firmware_module/module_rev3/src/rs458.c b/software/firmware_module/module_universal/src/rs458.c similarity index 96% rename from software/firmware_module/module_rev3/src/rs458.c rename to software/firmware_module/module_universal/src/rs458.c index 2fb86a0..519cf0f 100644 --- a/software/firmware_module/module_rev3/src/rs458.c +++ b/software/firmware_module/module_universal/src/rs458.c @@ -13,7 +13,7 @@ void rs485_init() { // init I/O DDRD &= ~(1 << PD0); // RX is INPUT - DDRD |= (1 << PD3) | (1 << PD2) | (1 << PD1); // BUS_DIR & TX is OUTPUT + DDRD |= (1 << PIN_RS485_RXE) | (1 << PIN_RS485_TXE) | (1 << PD1); // BUS_DIR & TX is OUTPUT PORTD &= 0xE0; // clear PD0-PD4 // init UART UBRRH = (BAUDRATE >> 8); @@ -25,7 +25,7 @@ void rs485_init() // send byte over rs485 void rs485_send_c(char data) { - PORTD |= (1 << PD2) | (1 << PD3); // set transciever to transmitt + PORTD |= (1 << PIN_RS485_TXE) | (1 << PIN_RS485_RXE); // set transciever to transmitt while (!(UCSRA & (1 << UDRE))) ; // wait until buffer is empty UCSRA = (1 << TXC); // clear transmit Complete bit @@ -33,7 +33,7 @@ void rs485_send_c(char data) while (!(UCSRA & (1 << TXC))) { }; // wait until transmitt complete - PORTD &= ~((1 << PD2) | (1 << PD3)); // set transciever back to receive + PORTD &= ~((1 << PIN_RS485_TXE) | (1 << PIN_RS485_RXE)); // set transciever back to receive } // receive without timeout diff --git a/software/firmware_module/module_rev0/src/rs485.h b/software/firmware_module/module_universal/src/rs485.h similarity index 100% rename from software/firmware_module/module_rev0/src/rs485.h rename to software/firmware_module/module_universal/src/rs485.h