From aaa925eeb7ed555d225c194629a3342e5b6a5e7f Mon Sep 17 00:00:00 2001 From: Gregor Riepl Date: Mon, 11 Jan 2021 00:25:02 +0100 Subject: [PATCH] Added power interface test program --- poweriface/firmware/Makefile | 115 ++++++++++++++++ poweriface/firmware/can_config.h | 79 +++++++++++ poweriface/firmware/main.c | 219 +++++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 poweriface/firmware/Makefile create mode 100644 poweriface/firmware/can_config.h create mode 100644 poweriface/firmware/main.c diff --git a/poweriface/firmware/Makefile b/poweriface/firmware/Makefile new file mode 100644 index 0000000..38e056f --- /dev/null +++ b/poweriface/firmware/Makefile @@ -0,0 +1,115 @@ +PRG = poweriface +AVRCANLIB_PATH = ../../avr-can-lib +OBJ = main.o \ + $(AVRCANLIB_PATH)/src/at90can_buffer.o \ + $(AVRCANLIB_PATH)/src/at90can.o \ + $(AVRCANLIB_PATH)/src/at90can_disable_dyn_filter.o \ + $(AVRCANLIB_PATH)/src/at90can_error_register.o \ + $(AVRCANLIB_PATH)/src/at90can_get_buf_message.o \ + $(AVRCANLIB_PATH)/src/at90can_get_dyn_filter.o \ + $(AVRCANLIB_PATH)/src/at90can_get_message.o \ + $(AVRCANLIB_PATH)/src/at90can_send_buf_message.o \ + $(AVRCANLIB_PATH)/src/at90can_send_message.o \ + $(AVRCANLIB_PATH)/src/at90can_set_dyn_filter.o \ + $(AVRCANLIB_PATH)/src/at90can_set_mode.o \ + $(AVRCANLIB_PATH)/src/can_buffer.o +# These were defined for the avr-can-lib, but they really, really +# shouldn't be used there because they affect the whole program. +OPTIMIZE = -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +PROGRAMMER = usbtiny + +MCU_TARGET = atmega32m1 +DEFS = -DF_CPU=16000000UL -DHAS_CAN_CONFIG_H + +INCLUDES = -I. -I$(AVRCANLIB_PATH) +LIBS = + +LFUSE = 0xdf +HFUSE = 0xd9 +EFUSE = 0xfe + +EXTRA_CLEAN_FILES = + +# You should not have to change anything below here. + +CC = avr-gcc +DUDE = avrdude + +HOST_CC = gcc +HOST_CFLAGS = -O0 -g +HOST_LDFLAGS = + +# Override is only needed by avr-lib build system. + +override CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) $(INCLUDES) +override LDFLAGS = -Wl,-Map,$(PRG).map + +DUDEFLAGS = -c $(PROGRAMMER) -p $(MCU_TARGET) + +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump + +all: $(PRG).elf lst text eeprom + +$(PRG).elf: $(OBJ) $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + rm -f $(OBJ) $(PRG).elf $(PRG).lst $(PRG).map $(PRG).bin $(PRG).hex $(PRG).srec $(PRG)_eeprom.bin $(PRG)_eeprom.hex $(PRG)_eeprom.srec $(EXTRA_CLEAN_FILES) + +lst: $(PRG).lst + +%.lst: %.elf + $(OBJDUMP) -h -S $< > $@ + +# Rules for flashing, EEPROM writing and fuse bit setting + +flash: $(PRG).hex $(PRG)_eeprom.hex + $(DUDE) $(DUDEFLAGS) -U eeprom:w:$(PRG)_eeprom.hex:i -U flash:w:$(PRG).hex:i + +flashprg: $(PRG).hex + $(DUDE) $(DUDEFLAGS) -U flash:w:$(PRG).hex:i + +flasheep: $(PRG)_eeprom.hex + $(DUDE) $(DUDEFLAGS) -U eeprom:w:$(PRG)_eeprom.hex:i + +fuse: + $(DUDE) $(DUDEFLAGS) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m -U efuse:w:$(EFUSE):m + +# Rules for building the .text rom images + +text: hex bin srec + +hex: $(PRG).hex +bin: $(PRG).bin +srec: $(PRG).srec + +%.hex: %.elf + $(OBJCOPY) -j .text -j .data -O ihex $< $@ + +%.srec: %.elf + $(OBJCOPY) -j .text -j .data -O srec $< $@ + +%.bin: %.elf + $(OBJCOPY) -j .text -j .data -O binary $< $@ + +# Rules for building the .eeprom rom images + +eeprom: ehex ebin esrec + +ehex: $(PRG)_eeprom.hex +ebin: $(PRG)_eeprom.bin +esrec: $(PRG)_eeprom.srec + +%_eeprom.hex: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \ + || { echo empty $@ not generated; exit 0; } + +%_eeprom.srec: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \ + || { echo empty $@ not generated; exit 0; } + +%_eeprom.bin: %.elf + $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \ + || { echo empty $@ not generated; exit 0; } + diff --git a/poweriface/firmware/can_config.h b/poweriface/firmware/can_config.h new file mode 100644 index 0000000..547b9db --- /dev/null +++ b/poweriface/firmware/can_config.h @@ -0,0 +1,79 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define CAN_CONFIG_LOADED + +// ----------------------------------------------------------------------------- +/* Global settings for building the can-lib and application program. + * + * The following two #defines must be set identically for the can-lib and + * your application program. They control the underlying CAN struct. If the + * settings disagree, the underlying CAN struct will be broken, with + * unpredictable results. + * If can.h detects that any of the #defines is not defined, it will set them + * to the default values shown here, so it is in your own interest to have a + * consistent setting. Ommiting the #defines in both can-lib and application + * program will apply the defaults in a consistent way too. + * + * Select if you want to use 29 bit identifiers. + */ +#define SUPPORT_EXTENDED_CANID 1 + +/* Select if you want to use timestamps. + * Timestamps are sourced from a register internal to the AT90CAN. + * Selecting them on any other controller will have no effect, they will + * be 0 all the time. + */ +#define SUPPORT_TIMESTAMPS 0 + + +// ----------------------------------------------------------------------------- +/* Global settings for building the can-lib. + * + * Select ONE CAN controller for which you are building the can-lib. + */ +#define SUPPORT_MCP2515 0 +#define SUPPORT_AT90CAN 1 +#define SUPPORT_SJA1000 0 + + +// ----------------------------------------------------------------------------- +/* Setting for MCP2515 + * + * Declare which pins you are using for communication. + * Remember NOT to use them in your application! + * It is a good idea to use bits from the port that carries MOSI, MISO, SCK. + */ +#define MCP2515_CS B,4 +#define MCP2515_INT B,2 + +// ----------------------------------------------------------------------------- +// Setting for SJA1000 + +#define SJA1000_INT E,0 +#define SJA1000_MEMORY_MAPPED 1 + +// memory-mapped interface +#define SJA1000_BASE_ADDR 0x8000 // for ATMega162 + +/* +// port-interface +#define SJA1000_WR D,6 +#define SJA1000_RD D,7 + +#define SJA1000_ALE E,1 +#define SJA1000_CS C,0 +#define SJA1000_DATA A +*/ + +// ----------------------------------------------------------------------------- +// Setting for AT90CAN + +// Number of CAN messages which are buffered in RAM additinally to the MObs +#define CAN_RX_BUFFER_SIZE 16 +#define CAN_TX_BUFFER_SIZE 8 + +// only available if CAN_TX_BUFFER_SIZE > 0 +#define CAN_FORCE_TX_ORDER 1 + +#endif // CONFIG_H diff --git a/poweriface/firmware/main.c b/poweriface/firmware/main.c new file mode 100644 index 0000000..68aff94 --- /dev/null +++ b/poweriface/firmware/main.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include + +#if ENABLE_SLEEP +#include +#endif +#if ENABLE_WATCHDOG +#include +#endif + +// Global state +struct { +#if ENABLE_SHUTDOWN + // Main loop run state (1 = running, 0 = shutdown) + uint8_t run; +#endif + // Message ready? + uint8_t ready; + // Receive buffer + uint8_t buffer[8]; + // Message address + uint32_t address; +} state; //__attribute__ ((section (".noinit"))); + +inline void init(); // __attribute__((always_inline)); +inline void shutdown(); // __attribute__((always_inline)); + +inline void init() { + // Disable interrupts before init + cli(); + + // Set state + //state.run = 0; + +#if ENABLE_WATCHDOG + // Enable watchdog timer + wdt_enable(WDTO_500MS); +#endif + + // Set clock divider to 1 after startup + // This is an alternative to clearing the CLKDIV8 fuse + // CLKPR = _BV(CLKPCE); + // CLKPR = 0x00; + + // Enable GPIO and set low +// PORTB |= _BV(PB5); + PORTB &= ~(_BV(PB2) | _BV(PB3) | _BV(PB4)); + DDRB |= (_BV(PB2) | _BV(PB3) | _BV(PB4)); +} + +inline void shutdown() { + // Disable interrupts + // Not needed when not using sleep, the compiler will generate a cli+busy loop +#if ENABLE_SLEEP + cli(); +#endif + +#if ENABLE_WATCHDOG + // Disable watchdog + wdt_disable(); +#endif +} + +// UART bit-uart_bang 8 bits on the debug port, LSB first, 1 stop bit, no parity, 9600 baud +void uart_bang(uint8_t data) { + // Start bit: low + PORTB &= ~_BV(PB4); + _delay_us(102); + for (uint8_t i = 0; i < 8; i++) { + // data bit + if (data & (_BV(i))) { + PORTB |= _BV(PB4); + } else { + PORTB &= ~_BV(PB4); + } + _delay_us(102); + } + // Stop bit: high (and stay there after) + PORTB |= _BV(PB4); + _delay_us(102); +} + +// Bit-bang pattern (LSB first) out the data line and strobe +uint16_t bang(uint16_t data) { + uint16_t in = 0; + for (uint8_t i = 0; i < 16; i++) { + // Data bit + if (data & 1) { + PORTB |= _BV(PB4); + } else { + PORTB &= ~_BV(PB4); + } + // Clock + PORTB |= _BV(PB2); + _delay_us(4); + PORTB &= ~_BV(PB2); + _delay_us(4); + // Read + if (PINB & _BV(PB5)) { + in |= 1; + } + // Shift + data >>= 1; + in <<= 1; + } + // Strobe + PORTB |= _BV(PB3); + _delay_us(4); + PORTB &= ~_BV(PB3); + // Clear + PORTB &= ~_BV(PB4); + return in; +} + +int main() { + // Initialize hardware + init(); + // Initialize CAN interface + can_init(BITRATE_125_KBPS); + // Set up command filter + can_set_mode(NORMAL_MODE); + can_filter_t filter = { + .id = 0x100, + .mask = 0x1ff, + .flags = { + .rtr = 0, + .extended = 0, + }, + }; + can_set_filter(0, &filter); + // wait before start + //_delay_ms(10000); + + // Enable interrupts to start reception + sei(); + + // Main loop +#if ENABLE_SHUTDOWN + state.run = 1; + while (state.run) { +#else + while (1) { +#endif +#if 0 + // Test pattern + for (uint8_t i = 0; i < 5; i++) { + bang(0x0080 >> i); + _delay_ms(1000); + bang(0x0000); + _delay_ms(1000); + } +#else + // Check for new messages + if (can_check_message()) { + can_t msg = { 0 }; + uint8_t status = can_get_message(&msg); +// uart_bang(status); + uint16_t in = 0; + if (status != 0 && msg.id == 0x100 && msg.length == 1) { +// uart_bang(msg.id & 0xff); +// uart_bang((msg.id >> 8) & 0xff); +// uart_bang((msg.id >> 16) & 0xff); +// uart_bang((msg.id >> 24) & 0xff); +// uart_bang(msg.flags.rtr); +// uart_bang(msg.flags.extended); +// uart_bang(msg.length); +// for (uint8_t i = 0; i < msg.length; i++) { +// uart_bang(msg.data[i]); +// } + //uint16_t out = msg.data[0] | ((uint16_t) msg.data[1] << 8); + if (msg.data[0] < 5) { + uint16_t out = 0x0080 >> msg.data[0]; + bang(out); + _delay_ms(3000); + in = bang(0x0000); + } + //in = bang(0xffff); + } + if (can_check_free_buffer()) { + can_t msg = { + .id = 0x11, + .flags = { + .rtr = 0, + .extended = 0, + }, + .length = 5, + .data = { 0xc0, 0xff, 0xee, in & 0xff, (in >> 8) & 0xff, }, + }; + uint8_t status = can_send_message(&msg); +// uart_bang(status); + } else { +// uart_bang(0xff); + } + } +#endif + +#if ENABLE_SLEEP + // Let the MCU take a nap + set_sleep_mode(SLEEP_MODE_IDLE); + sleep_mode(); +#endif + } + + // Disable all peripherals and interrupts + shutdown(); + + // Power the MCU down +#if ENABLE_SLEEP + while (1) { + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_mode(); + } + __builtin_unreachable(); +#endif +} +