From 70db631c93f0492b04e082a780a7350a9a42df82 Mon Sep 17 00:00:00 2001 From: hugogogo Date: Sat, 15 Mar 2025 16:39:29 +0100 Subject: [PATCH] add rush 00 --- README.md | 1 + rush00 | 1 - rush_00/Makefile | 83 +++++++++++++++++++++++++++++++++ rush_00/README.md | 37 +++++++++++++++ rush_00/bitmanip.h | 53 +++++++++++++++++++++ rush_00/game.c | 18 ++++++++ rush_00/interrupt.h | 8 ++++ rush_00/interrupts.c | 66 ++++++++++++++++++++++++++ rush_00/main.c | 52 +++++++++++++++++++++ rush_00/roles.c | 22 +++++++++ rush_00/rush_header.h | 72 +++++++++++++++++++++++++++++ rush_00/timer.h | 27 +++++++++++ rush_00/twi.c | 105 ++++++++++++++++++++++++++++++++++++++++++ rush_00/usart.h | 31 +++++++++++++ rush_00/utils.h | 23 +++++++++ 15 files changed, 598 insertions(+), 1 deletion(-) delete mode 160000 rush00 create mode 100644 rush_00/Makefile create mode 100644 rush_00/README.md create mode 100644 rush_00/bitmanip.h create mode 100644 rush_00/game.c create mode 100644 rush_00/interrupt.h create mode 100644 rush_00/interrupts.c create mode 100644 rush_00/main.c create mode 100644 rush_00/roles.c create mode 100644 rush_00/rush_header.h create mode 100644 rush_00/timer.h create mode 100644 rush_00/twi.c create mode 100644 rush_00/usart.h create mode 100644 rush_00/utils.h diff --git a/README.md b/README.md index 98d53c1..e0f5ea9 100644 --- a/README.md +++ b/README.md @@ -89,4 +89,5 @@ avrdude done. Thank you. ## screen +- (optional) `export XTERM=xterm` - `screen /dev/ttyUSB0 115200` diff --git a/rush00 b/rush00 deleted file mode 160000 index e4ea057..0000000 --- a/rush00 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e4ea057ebfb9c9d8cd6d56100e94db56d606da6a diff --git a/rush_00/Makefile b/rush_00/Makefile new file mode 100644 index 0000000..4da4261 --- /dev/null +++ b/rush_00/Makefile @@ -0,0 +1,83 @@ +# frequency of the CPU (in Hz) +F_CPU = 16000000UL +TARGET = main +CC = avr-gcc +INCLUDES = -I. + +AVRGCC_MCU_TYPE = atmega328p +AVRDUDE_MCU_TYPE = m328p +BAUD_RATE = 115200 +PROGRAMMER_ID = arduino + +# original firmware dump +DUMP_ORI = ../../ressources/dump_atmega328p_ori.hex + +# Find all C source files in the current directory +SRC = $(wildcard *.c) +# Generate object file names from source file names +OBJ = $(SRC:.c=.o) + +# Compiler flags +CFLAGS = -mmcu=$(AVRGCC_MCU_TYPE) -D F_CPU=$(F_CPU) $(INCLUDES) -Os + +# Detect OS +OS := $(shell uname -s) + +# Set Serial Port based on OS +ifeq ($(OS), Darwin) # macOS + SERIAL_PORT_1 := /dev/tty.usbserial-310 # $(shell ls /dev/tty.usb* 2>/dev/null | head -n 1) # should be : /dev/tty.usbserial-310 + SERIAL_PORT_2 := /dev/tty.usbserial-110 +else ifeq ($(OS), Linux) # Ubuntu/Linux + SERIAL_PORT_1 = /dev/ttyUSB0 # $(shell ls /dev/ttyUSB* 2>/dev/null | head -n 1) # should be : /dev/ttyUSB0 + SERIAL_PORT_2 = /dev/ttyUSB1 +endif + +## +## RULES +## + +all: hex flash + +hex: $(TARGET).hex + +# Compile each .c file to corresponding .o file +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# Link all object files to create the binary +# avr-gcc : compiler for AVR microcontrollers, https://gcc.gnu.org/wiki/avr-gcc#Using_avr-gcc, https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html +# -mmcu : the target microcontrolle (ATmega328P) +# -D : defines a preprocessor macro (#define F_CPU ) +# -o : output filename +$(TARGET).bin: $(OBJ) + $(CC) -mmcu=$(AVRGCC_MCU_TYPE) $(OBJ) -o $(TARGET).bin + +# Convert binary to hex for flashing +# avr-objcopy : man avr-jobcopy, https://linux.die.net/man/1/avr-objcopy +# -O : specifies output format, Intel HEX +# some formats (list not found in any doc) : +# -O ihex : Used for flashing AVR chips +# -O binary : Raw binary file +# -O elf32-avr : Used for debugging +$(TARGET).hex: $(TARGET).bin + avr-objcopy -O ihex $(TARGET).bin $(TARGET).hex + +# Flash the hex file to the microcontroller +# avr-dude : AVR Downloader/UploaDEr, upload firmware, https://avrdudes.github.io/avrdude, v6.3 https://download-mirror.savannah.gnu.org/releases/avrdude/avrdude-doc-6.3.pdf +# -p : specifies the microcontroller -> m328p +# -c : programmer-id +# -b : baud rate for serial communication +# -P : the serial port, found with `ls /dev/tty*` -> /dev/ttyUSB0 +# -U : Uploads (w = write) the HEX firmware file to the flash memory +# (-v : verbose info dump) +flash: + avrdude -p $(AVRDUDE_MCU_TYPE) -c $(PROGRAMMER_ID) -b $(BAUD_RATE) -P $(SERIAL_PORT_1) -U flash:w:$(TARGET).hex + avrdude -p $(AVRDUDE_MCU_TYPE) -c $(PROGRAMMER_ID) -b $(BAUD_RATE) -P $(SERIAL_PORT_2) -U flash:w:$(TARGET).hex + +restore: + avrdude -p $(AVRDUDE_MCU_TYPE) -c $(PROGRAMMER_ID) -b $(BAUD_RATE) -P $(SERIAL_PORT) -U flash:w:$(DUMP_ORI) + +clean: + rm -f main.hex main.bin $(OBJ) + +.PHONY : all clean hex flash restore \ No newline at end of file diff --git a/rush_00/README.md b/rush_00/README.md new file mode 100644 index 0000000..18df929 --- /dev/null +++ b/rush_00/README.md @@ -0,0 +1,37 @@ +# Parallel Development Plan (chat gpt :p) + +| Task | Developer | Dependencies | +| ------------------------------------------- | ------------ | ----------------------------------------------- | +| Configure I2C (Master/Slave) | **Person A** | None | +| Configure GPIOs (Buttons & LEDs) | **Person B** | None | +| Implement button interrupt handling | **Person B** | Needs GPIO setup | +| Implement LED countdown sequence | **Person B** | Needs GPIO setup | +| Implement I2C send & receive functions | **Person A** | Needs I2C setup | +| Implement game state transitions | **Person A** | Needs I2C communication & button press handling | +| Implement reaction detection & winner logic | **Person A** | Needs game state management & button interrupts | +| Implement LED signals for winner/loser | **Person B** | Needs reaction detection | +| Implement game reset logic | **Person A** | Needs winner detection | +| Sync both MCUs on game start | **Person A** | Needs I2C and button handling | + +## How Each Person Works in Parallel + +### **Person A: I2C & Game Logic** + +- Starts with I2C initialization and communication functions. +- Implements game state management (waiting, countdown, reaction, result). +- Manages synchronization between the two MCUs. +- Detects the winner and resets the game. + +### **Person B: Buttons & LEDs** + +- Sets up buttons and interrupts. +- Implements debounce mechanism to avoid false triggers. +- Handles the countdown sequence using LEDs. +- Displays winner/loser using LEDs. + +## Collaboration Strategy + +1. **Step 1:** Work independently on separate modules. +2. **Step 2:** Integrate button presses with game state transitions. +3. **Step 3:** Test button presses and countdown independently. +4. **Step 4:** Merge both parts and test full game logic with I2C communication. diff --git a/rush_00/bitmanip.h b/rush_00/bitmanip.h new file mode 100644 index 0000000..4df44d1 --- /dev/null +++ b/rush_00/bitmanip.h @@ -0,0 +1,53 @@ +#ifndef BITMANIP_H +#define BITMANIP_H + +#include "utils.h" + +// Bit operations on registers +#define SET(register, bit) (register |= (1 << (bit))) +#define CLEAR(register, bit) (register &= ~(1 << (bit))) +#define TEST(register, bit) (register & (1 << (bit))) +#define TOGGLE(register, bit) (register ^= (1 << (bit))) + +// Get arguments from tuple-like definitions +#define ARG_1(v1, v2) v1 +#define ARG_2(v1, v2) v2 +#define GET_PORT(args) ARG_1 args +#define GET_BIT(args) ARG_2 args +// // version with "LED1 B, D1" without parenthesis +// #define ARG_1(v1, ...) v1 +// #define ARG_2(v1, v2, ...) v2 +// #define GET_PORT(...) ARG_1(__VA_ARGS__) +// #define GET_BIT(...) ARG_2(__VA_ARGS__) + +// Actions on elements +// #define SET_ELEM(...) SET(CONCAT(PORT, GET_PORT(__VA_ARGS__)), GET_BIT(__VA_ARGS__)) // version for "LED1 B, D1" without parenthesis +#define SET_ELEM(elem) SET(CONCAT(PORT, GET_PORT(elem)), GET_BIT(elem)) +#define CLEAR_ELEM(elem) CLEAR(CONCAT(PORT, GET_PORT(elem)), GET_BIT(elem)) +#define TEST_ELEM(elem) TEST(CONCAT(PORT, GET_PORT(elem)), GET_BIT(elem)) +#define TOGGLE_ELEM(elem) TOGGLE(CONCAT(PORT, GET_PORT(elem)), GET_BIT(elem)) + +#define MODE_OUTPUT(elem) SET(CONCAT(DDR, GET_PORT(elem)), GET_BIT(elem)) +#define MODE_INPUT(elem) CLEAR(CONCAT(DDR, GET_PORT(elem)), GET_BIT(elem)) +#define TOGGLE_PIN(elem) SET(CONCAT(PIN, GET_PORT(elem)), GET_BIT(elem)) +#define TEST_PIN(elem) (TEST(CONCAT(PIN, GET_PORT(elem)), GET_BIT(elem))) +#define IS_PIN_SET(elem) (TEST_PIN(elem) == 0) +#define IS_PIN_CLEAR(elem) (TEST_PIN(elem) == 1) + +// Bit definitions +#define D1 0 +#define D2 1 +#define D3 2 +#define D4 4 +#define SW1 2 +#define SW2 4 + +// Elements (port, bit) +#define LED1 (B, D1) +#define LED2 (B, D2) +#define LED3 (B, D3) +#define LED4 (B, D4) +#define BUTTON1 (D, SW1) +#define BUTTON2 (D, SW2) + +#endif // BITMANIP_H \ No newline at end of file diff --git a/rush_00/game.c b/rush_00/game.c new file mode 100644 index 0000000..718f68d --- /dev/null +++ b/rush_00/game.c @@ -0,0 +1,18 @@ +#include "rush_header.h" + +void timer() { + flash_led(D3); + _delay_ms(500); + flash_led(D3); + _delay_ms(500); + flash_led(D3); + _delay_ms(500); + flash_led(D3); + _delay_ms(500); + turn_on_led(D3); +} + +void launch_game() { + // timer(); + write_one_byte_data(0x01); +} diff --git a/rush_00/interrupt.h b/rush_00/interrupt.h new file mode 100644 index 0000000..dccc1c5 --- /dev/null +++ b/rush_00/interrupt.h @@ -0,0 +1,8 @@ +#ifndef INTERRUPT_H +#define INTERRUPT_H + +// 7.3.1 : SREG – AVR Status Register +#define ENABLE_GLOBAL_INTERRUPT (1<<7) +#define DISABLE_GLOBAL_INTERRUPT (0<<7) + +#endif // INTERRUPT_H \ No newline at end of file diff --git a/rush_00/interrupts.c b/rush_00/interrupts.c new file mode 100644 index 0000000..514f046 --- /dev/null +++ b/rush_00/interrupts.c @@ -0,0 +1,66 @@ +#include "rush_header.h" + +volatile Role role = WAITING; + +void setup_button_interrupt() { // interruption SW1 (PD2) + EICRA |= (1 << ISC01); // trigger on falling edge for INT0 + EIMSK |= (1 << INT0); // Activate INT0 + + SREG |= (1 << SREG_I); +} + +ISR(INT0_vect) { // interruption SW1 (PD2) + if (!TEST_PIN(BUTTON1)) { + _delay_ms(30); + flash_led(D4); + if (role == MASTER) { + // write_one_byte_data(0x01); + read_one_byte_data(); + } else if (role == SLAVE) { + // PORTD &= ~(1 << PD2); // Pull PD2 low (active LOW signal) + // _delay_ms(10); // Give master time to catch the signal + // PORTD |= (1 << PD2); // Reset line HIGH + } + } +} + +ISR(TWI_vect) { // interruption slave receive data, Table 12-1 : 25, 2-wire Serial Interface + uint8_t status = TWSR & MASK_WITHOUT_LAST_3; + + switch (status) { + + // Slave Receiver cases + case TW_SR_SLA_ACK: // SLA+W received, ACK sent + // flash_led(D4); + turn_on_led_2_light(); + break; + case TW_SR_DATA_ACK: // data received, ACK sent + if (role == WAITING) { + role = SLAVE; + } + received_data = TWDR; // store received data + break; + case TW_SR_STOP: // STOP or REPEATED START + break; + case TW_SR_DATA_NACK: // data received but slave answered NACK (no ACK an means end of communication or error) + break; + + // Slave Transmitter cases + case TW_ST_SLA_ACK: // SLA+R received, ACK sent - First byte to be transmitted + flash_led(D4); + TWDR = 0x10; // send first data byte + break; + case TW_ST_DATA_ACK: // data sent, ACK received - Additional bytes can be sent + TWDR = 0x10; // send next data byte + break; + case TW_ST_DATA_NACK: // data sent, NACK received - No more data requested + break; // master doesn't want more data + case TW_ST_LAST_DATA: // last data byte sent, ACK received + break; // All data sent + + default: + break; + } + + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA) | (1 << TWIE); // Clear interrupt flag and enable next transfer +} \ No newline at end of file diff --git a/rush_00/main.c b/rush_00/main.c new file mode 100644 index 0000000..3777c89 --- /dev/null +++ b/rush_00/main.c @@ -0,0 +1,52 @@ +#include "rush_header.h" + +#define BLINK_PRESCALE_VALUE 1024 // can be 1, 8, 64, 256, 1024 +#define BLINK_DUTY_CYCLE 10 + +void flash_led(int bit) { + SET(DDRB, bit); + SET(PORTB, bit); + _delay_ms(100); + CLEAR(PORTB, bit); +} +void turn_on_led(int bit) { + SET(DDRB, bit); + SET(PORTB, bit); +} +void turn_on_led_2_light() { + uint8_t duty = 10; + // Set PB1 (OC1A) as output + DDRB |= (1 << PB1); + + // Configure Timer1 for Fast PWM, 8-bit mode + TCCR1A = (1 << COM1A1) | (1 << WGM10); // Clear OC1A on compare match, Fast PWM + TCCR1B = (1 << WGM12) | (1 << CS11); // Prescaler 8 + + // Set duty cycle (0-255, where 255 = fully ON, 0 = fully OFF) + OCR1A = duty; +} +void turn_off_led(int bit) { + SET(DDRB, bit); + CLEAR(PORTB, bit); +} +void blink_led_2(int period) { + MODE_OUTPUT(LED2); + SET(TCCR1A, WGM11); // Table 16-4 : set timer in Fast PWM (Pulse With Modulation) mode with TOP = ICR1 (Mode 14) + SET(TCCR1B, WGM12); + SET(TCCR1B, WGM13); + SET(TCCR1A, COM1A1); // Table 16-2 : non-inverting mode, the LED will be ON for DUTY_CYCLE% of the time (CLEAR OC1A on compare match, SET OC1A at BOTTOM) + ICR1 = TIME_MS(period, BLINK_PRESCALE_VALUE); // Table 16-4 : set the period (compare TOP value) + OCR1A = TIME_MS(PERCENT(BLINK_DUTY_CYCLE, period), BLINK_PRESCALE_VALUE); // 16.9.3 : set the duty cycle to DUTY_CYCLE% of the time on channel A -> OC1A (alternate function of PORTB1, aka LED2) is cleared when TCNT1 (the counter value) equals OCR1A + TCCR1B |= (PRESCALE_SET(BLINK_PRESCALE_VALUE)); // start the timer with the prescaler +} + +int main() { + MODE_INPUT(BUTTON1); // set as input + SET_ELEM(BUTTON1); // pull-up resistor on + + twi_init_slave(); + get_role(); + setup_button_interrupt(); + // launch_game(); + while (1); +} diff --git a/rush_00/roles.c b/rush_00/roles.c new file mode 100644 index 0000000..c744f82 --- /dev/null +++ b/rush_00/roles.c @@ -0,0 +1,22 @@ +#include "rush_header.h" + +void get_role() { + int end = FALSE; + while(!end) { + _delay_ms(50); + if (role == SLAVE) { + end = TRUE; + // MODE_OUTPUT(BUTTON1); + continue; + } + if (!IS_PIN_SET(BUTTON1)) { + continue; + } + end = TRUE; + turn_on_led(D1); + role = MASTER; + twi_stop_slave(); + twi_init_master(); + write_one_byte_data(0x01); + } +} \ No newline at end of file diff --git a/rush_00/rush_header.h b/rush_00/rush_header.h new file mode 100644 index 0000000..beb749c --- /dev/null +++ b/rush_00/rush_header.h @@ -0,0 +1,72 @@ +#ifndef RUSH_HEADER_H +#define RUSH_HEADER_H + +#include +#include +#include +#include + +#include "utils.h" +#include "bitmanip.h" +#include "timer.h" +#include "usart.h" +#include "interrupt.h" + +// TWI +// table 22-7 : prescale sets +#define TWI_PRESCALE_VALUE 1 // can be 1, 4, 16, 64 +#define TWI_PRESCALE_SET(value) \ + ((value) == 1 ? (0< 0) { + timeout--; + _delay_us(10); + } + + flash_led(D2); // After TWINT set or timeout + + if (timeout == 0) { + // Timeout occurred, slave didn't respond + flash_led(D3); // Indicate timeout + TWCR = SEND_STOP_CONDITION; + return 0xFF; // Error code for timeout + } + + uint8_t status = TWSR & MASK_WITHOUT_LAST_3; + if (status != TW_MR_DATA_ACK && status != TW_MR_DATA_NACK) { + TWCR = SEND_STOP_CONDITION; + return 0; + } + flash_led(D4); // Indicate status error + return TWDR; +} + +void twi_stop() { + TWCR = SEND_STOP_CONDITION; +} + +void write_one_byte_data(uint8_t data) { + twi_start(); + twi_send_addr((SLAVE_ADDRESS << 1) | TW_WRITE); // Send Slave address with Write bit + twi_write(data); // write data byte + twi_stop(); +} +uint8_t read_one_byte_data() { + twi_start(); + twi_send_addr((SLAVE_ADDRESS << 1) | TW_READ); // Send Slave address with Write bit + uint8_t data = twi_read(FALSE); // Read data with NACK (only one byte expected) + twi_stop(); + return data; +} \ No newline at end of file diff --git a/rush_00/usart.h b/rush_00/usart.h new file mode 100644 index 0000000..b930036 --- /dev/null +++ b/rush_00/usart.h @@ -0,0 +1,31 @@ +#ifndef USART_H +#define USART_H + +#define BAUD_PRESCALER(usart_baudrate) (DIV_ROUND_CLOSEST(F_CPU, (16 * usart_baudrate)) - 1) +// Table 20-8 : Mode Selection (USART Mode SELect) +#define ASYNCHRONOUS (0<= 'A' && (ch) <= 'Z') || ((ch) >= 'a' && (ch) <= 'z') ? ((ch) ^ (1 << 5)) : (ch)) + +// boolean +#define TRUE 1 +#define FALSE 0 + +#endif // UTILS_H \ No newline at end of file