diff --git a/.vscode/settings.json b/.vscode/settings.json index 53ad538..cce9b3b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "utils.h": "c", "bitmanip.h": "c", "rush_header.h": "c", - "twi.h": "c" + "twi.h": "c", + "header.h": "c" } } diff --git a/Makefile b/Makefile index 3054d8b..e6096a9 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,14 @@ 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) @@ -30,13 +38,19 @@ 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: $(TARGET).c - $(CC) -mmcu=$(AVRGCC_MCU_TYPE) -D F_CPU=$(F_CPU) $(INCLUDES) $(TARGET).c -Os -o $(TARGET).bin +$(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) : @@ -46,6 +60,7 @@ $(TARGET).bin: $(TARGET).c $(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 @@ -60,6 +75,6 @@ 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 + rm -f main.hex main.bin $(OBJ) .PHONY : all clean hex flash restore \ No newline at end of file diff --git a/module03/ex03/colors.c b/module03/ex03/colors.c new file mode 100644 index 0000000..c801d47 --- /dev/null +++ b/module03/ex03/colors.c @@ -0,0 +1,77 @@ +#include "header.h" + + +// Table 14-9 : Port D Pins Alternate Functions +// OC2B (PD3) : Red +// OC0B (PD5) : Green +// OC0A (PD6) : Blue +void init_rgb() { + MODE_OUTPUT(RGB5_RED); + MODE_OUTPUT(RGB5_GEEN); + MODE_OUTPUT(RGB5_BLUE); + + TCCR0A = 0; // 15.9.1 : Clear Timer0 control register A + TCCR0B = 0; // 15.9.2 : Clear Timer0 control register B + TCCR2A = 0; // Clear Timer2 control register A + + TCCR0A |= (1 << WGM00) | (1 << WGM01); // Table 15-8 : Timer0 in Fast PWM mode + TCCR2A |= (1 << WGM20) | (1 << WGM21); // Table 18-8 : Timer2 in Fast PWM mode + + TCCR0A |= (1 << COM0A1); // Table 15-3 : BLUE - Timer0 Non-inverting mode for Blue (OC0A -> PD6) + TCCR0A |= (1 << COM0B1); // Table 15-6 : GREEN - Timer0 Non-inverting mode for Green (OC0B -> PD5) + TCCR2A |= (1 << COM2B1); // Table 18-6 : RED - Timer2 Non-inverting mode for Red (OC2B -> PD3) + + TCCR0B |= (1 << CS01); // Table 15-9 : Prescaler 8 → ~8kHz PWM frequency + TCCR2B |= (1 << CS21); // Table 18-9 : Prescaler 8 → ~8kHz PWM frequency + + OCR0A = 0; // Blue OFF + OCR0B = 0; // Green OFF + OCR2B = 0; // Red OFF +} + +void set_rgb(uint8_t r, uint8_t g, uint8_t b) { + OCR2B = r; // Red (PD3, Timer2 OCR2B) + OCR0B = g; // Green (PD5, Timer0 OCR0B) + OCR0A = b; // Blue (PD6, Timer0 OCR0A) +} + +// convert hex to int : https://stackoverflow.com/a/39052987 +uint8_t hex_to_int(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + return 0; +} + +// "<< 4" is equivalent to "* 16" +uint8_t get_red(char *input) { + if (input[0] != '#') { + return 0; + } + return (hex_to_int(input[1]) << 4) + hex_to_int(input[2]); +} +uint8_t get_green(char *input) { + if (input[0] != '#') { + return 0; + } + return (hex_to_int(input[3]) << 4) + hex_to_int(input[4]); +} +uint8_t get_blue(char *input) { + if (input[0] != '#') { + return 0; + } + return (hex_to_int(input[5]) << 4) + hex_to_int(input[6]); +} + +void set_color(char *color_input) { + uint8_t r = get_red(color_input); + uint8_t g = get_green(color_input); + uint8_t b = get_blue(color_input); + set_rgb(r, g, b); +} diff --git a/module03/ex03/header.h b/module03/ex03/header.h new file mode 100644 index 0000000..7a93bd3 --- /dev/null +++ b/module03/ex03/header.h @@ -0,0 +1,39 @@ +#ifndef HEADER_H +#define HEADER_H + +#include +#include +#include + +#include "utils.h" +#include "bitmanip.h" +#include "timer.h" +#include "usart.h" +#include "interrupt.h" + +// +// PROTOTYPES +// +// main.c +// colors.c +void init_rgb(); +void set_rgb(uint8_t r, uint8_t g, uint8_t b); +uint8_t get_red(char *input); +uint8_t get_green(char *input); +uint8_t get_blue(char *input); +uint8_t hex_to_int(char c); +void uart_init(); +void set_color(char *color_input); +// uart.c +void uart_init(); +char uart_rx(void); +void uart_tx(char c); +// void uart_printstr(const char* str); +void remove_last_character(); +void fill_str(char input); +int strlength(volatile char *str); +void reset_input(); +int is_valid_color_input(char c); + + +#endif // HEADER_H \ No newline at end of file diff --git a/module03/ex03/main.c b/module03/ex03/main.c index cdd82b9..501b7c7 100644 --- a/module03/ex03/main.c +++ b/module03/ex03/main.c @@ -1,94 +1,11 @@ -#include -#include -#include +#include "header.h" -#include "utils.h" -#include "bitmanip.h" - -// prototypes -void init_rgb(); -void set_rgb(uint8_t r, uint8_t g, uint8_t b); -uint8_t get_red(char *input); -uint8_t get_green(char *input); -uint8_t get_blue(char *input); -uint8_t hex_to_int(char c); - -// Table 14-9 : Port D Pins Alternate Functions -// OC2B (PD3) : Red -// OC0B (PD5) : Green -// OC0A (PD6) : Blue -void init_rgb() { - MODE_OUTPUT(RGB5_RED); - MODE_OUTPUT(RGB5_GEEN); - MODE_OUTPUT(RGB5_BLUE); - - TCCR0A = 0; // 15.9.1 : Clear Timer0 control register A - TCCR0B = 0; // 15.9.2 : Clear Timer0 control register B - TCCR2A = 0; // Clear Timer2 control register A - - TCCR0A |= (1 << WGM00) | (1 << WGM01); // Table 15-8 : Timer0 in Fast PWM mode - TCCR2A |= (1 << WGM20) | (1 << WGM21); // Table 18-8 : Timer2 in Fast PWM mode - - TCCR0A |= (1 << COM0A1); // Table 15-3 : BLUE - Timer0 Non-inverting mode for Blue (OC0A -> PD6) - TCCR0A |= (1 << COM0B1); // Table 15-6 : GREEN - Timer0 Non-inverting mode for Green (OC0B -> PD5) - TCCR2A |= (1 << COM2B1); // Table 18-6 : RED - Timer2 Non-inverting mode for Red (OC2B -> PD3) - - TCCR0B |= (1 << CS01); // Table 15-9 : Prescaler 8 → ~8kHz PWM frequency - TCCR2B |= (1 << CS21); // Table 18-9 : Prescaler 8 → ~8kHz PWM frequency - - OCR0A = 0; // Blue OFF - OCR0B = 0; // Green OFF - OCR2B = 0; // Red OFF -} - -void set_rgb(uint8_t r, uint8_t g, uint8_t b) { - OCR2B = r; // Red (PD3, Timer2 OCR2B) - OCR0B = g; // Green (PD5, Timer0 OCR0B) - OCR0A = b; // Blue (PD6, Timer0 OCR0A) -} - -// convert hex to int : https://stackoverflow.com/a/39052987 -uint8_t hex_to_int(char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'A' && c <= 'F') { - return c - 'A' + 10; - } - if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } - return 0; -} - -// "<< 4" is equivalent to "* 16" -uint8_t get_red(char *input) { - if (input[0] == '#') { - return (hex_to_int(input[1]) << 4) + hex_to_int(input[2]); - } - return 0; -} -uint8_t get_green(char *input) { - if (input[0] == '#') { - return (hex_to_int(input[3]) << 4) + hex_to_int(input[4]); - } - return 0; -} -uint8_t get_blue(char *input) { - if (input[0] == '#') { - return (hex_to_int(input[5]) << 4) + hex_to_int(input[6]); - } - return 0; -} // led RGB D5 must turns on in a loop of colors using PWM int main() { init_rgb(); + uart_init(); - char *input = "#FFFF00"; - uint8_t r = get_red(input); - uint8_t g = get_green(input); - uint8_t b = get_blue(input); - set_rgb(r, g, b); while (1); } + diff --git a/module03/ex03/uart.c b/module03/ex03/uart.c new file mode 100644 index 0000000..d8c563b --- /dev/null +++ b/module03/ex03/uart.c @@ -0,0 +1,121 @@ +#include "header.h" + +// MACROS +// USART +#define USART_BAUDRATE 115200 +#define INPUT_SIZE 7 + +// GLOBAL VARIABLES +volatile char color_input[INPUT_SIZE + 1] = {0}; +volatile int input_index = 0; + +// +// FUNCTIONS +// +void uart_init() { + UBRR0H = (unsigned char) (BAUD_PRESCALER(USART_BAUDRATE) >> 8); // 20.11.5 : UBRRnL and UBRRnH – USART Baud Rate Registers + UBRR0L = (unsigned char) BAUD_PRESCALER(USART_BAUDRATE); + UCSR0C |= ASYNCHRONOUS | PARITY_DISABLED | STOP_ONE_BIT | DATA_EIGHT_BIT; // 20.11.4 : set Frame Format + UCSR0B |= RECEIVER_ENABLED | TRANSMITTER_ENABLED | INTERRUPT_RECEIVER_ENABLED; // 20.11.3 : enable Receiver and Transmitter, and interrupt on receiver + SREG |= ENABLE_GLOBAL_INTERRUPT; // 7.3.1 : Status Register, bit 7 : I – Global Interrupt Enable +} + +char uart_rx(void) { + while (TEST(UCSR0A, RXC0) == 0); // 20.11.2 : do nothing until there are unread data in the receive buffer (UDRn), (RXCn flag in UCSRnA register set to 1 when buffer has data) + return UDR0; // 20.11.1 : get data in buffer, UDRn – USART I/O Data Register (read and write) +} + +void uart_tx(char c) { + while (TEST(UCSR0A, UDRE0) == 0); // 20.11.2 : do nothing until UDRn buffer is empty, (UDREn flag in UCSRnA register set to 1 when buffer empty) + UDR0 = (unsigned char) c; // 20.11.1 : Put data into buffer, UDRn – USART I/O Data Register (read and write) +} + +// void uart_printstr(const char* str) { +// while (*str) { +// uart_tx(*str); +// str++; +// } +// } + +ISR(USART_RX_vect) { // Table 12-7 : we select the code for USART Receive + char received_char = UDR0; // read received character + if (received_char == '\b' || received_char == 127) { // if backspace is received + if (input_index <= 0) { + return; + } + if (input_index >= INPUT_SIZE) { + return; + } + remove_last_character(); + input_index--; + uart_tx('\b'); // Move cursor back + uart_tx(' '); // Erase the character on screen + uart_tx('\b'); // Move cursor back again + } else if (received_char == '\n' || received_char == '\r') { // if enter is received + if (input_index != INPUT_SIZE) { + return; + } + set_color((char *)color_input); + reset_input(); + } else { // any other character + if (input_index >= INPUT_SIZE) { + return; + } + if (!is_valid_color_input(received_char)) { + return; + } + fill_str(received_char); + uart_tx(received_char); + input_index++; + } +} + +int is_valid_color_input(char c) { + if (input_index == 0) { + if (c == '#') { + return TRUE; + } else { + return FALSE; + } + } + if (input_index >= INPUT_SIZE) { + return FALSE; + } + if (c >= '0' && c <= '9') { + return TRUE; + } else if (c >= 'A' && c <= 'F') { + return TRUE; + } else if (c >= 'a' && c <= 'f') { + return TRUE; + } + return FALSE; +} + +void fill_str(char input) { + if (input_index >= INPUT_SIZE) { + return; + } + color_input[input_index] = input; +} + +void remove_last_character() { + color_input[input_index] = 0; +} + +int strlength(volatile char *str) { + int length = 0; + while (*str) { + length++; + str++; + } + return length; +} + +void reset_input() { + for(int i = 0; i < INPUT_SIZE; i++) { + color_input[i] = 0; + } + uart_tx('\r'); + uart_tx('\n'); + input_index = 0; +} \ No newline at end of file