diff --git a/ex02/Makefile b/ex02/Makefile new file mode 100644 index 0000000..9471541 --- /dev/null +++ b/ex02/Makefile @@ -0,0 +1,57 @@ +# Makefile +MCU=atmega328p +F_CPU=16000000 +CC=avr-gcc +OBJCOPY=avr-objcopy +WFLAGS=-Wall -Wextra +CFLAGS=--std=c99 -g -Os -mmcu=$(MCU) -ffunction-sections -fdata-sections +CPPFLAGS=-DF_CPU=$(F_CPU) -Iinclude +IFLAGS= +LDFLAGS=-Wl,--gc-sections +TARGET=main +SERIAL=-P /dev/ttyUSB0 -b 115200 + +SRC_DIR=src +OBJ_DIR=build + +SRC_FILES=main.c utils.c uart.c +OBJ_FILES=$(patsubst %.c,%.o,$(SRC_FILES)) + +SRC=$(addprefix $(SRC_DIR)/,$(SRC_FILES)) +OBJ=$(addprefix $(OBJ_DIR)/,$(OBJ_FILES)) + +ELF_FILE=$(OBJ_DIR)/$(TARGET).elf +HEX_FILE=$(OBJ_DIR)/$(TARGET).hex + +all: flash + +re: fclean all + +fclean: clean + rm -f $(HEX_FILE) + rm -f $(ELF_FILE) +clean: + rm -rf $(OBJ_DIR) + +hex: $(HEX_FILE) + +flash: hex + avrdude -p $(MCU) -c arduino -U flash:w:$(HEX_FILE):i $(SERIAL) + + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c + @mkdir -p $(shell dirname $@) + $(CC) $(CPPFLAGS) $(WFLAGS) $(CFLAGS) $(IFLAGS) -c $< -o $@ + +$(ELF_FILE): $(OBJ) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $@ + +$(HEX_FILE): $(ELF_FILE) + $(OBJCOPY) -j .text -j .data -O ihex $< $@ + +print: + @echo $(SRC) + @echo $(OBJ) + @echo $(ELF_FILE) + @echo $(HEX_FILE) + diff --git a/ex02/include/interupt.h b/ex02/include/interupt.h new file mode 100644 index 0000000..983edc1 --- /dev/null +++ b/ex02/include/interupt.h @@ -0,0 +1,14 @@ +#ifndef INTERUPT_H +#define INTERUPT_H + +#include + +static inline void my_sei(void) { + SREG |= _BV(SREG_I); +} + +static inline void my_cli(void) { + SREG &= ~_BV(SREG_I); +} + +#endif /* INTERUPT_H */ diff --git a/ex02/include/mystd.h b/ex02/include/mystd.h new file mode 100644 index 0000000..a757bb0 --- /dev/null +++ b/ex02/include/mystd.h @@ -0,0 +1,17 @@ +#ifndef MYSTDINT_H +#define MYSTDINT_H + +typedef unsigned int uint16_t; +typedef signed int int16_t; + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef uint8_t bool; + +#define true (1) +#define false (0) + +#define NULL ((void*)0) + +#endif /* MYSTDINT_H */ diff --git a/ex02/include/timer0.h b/ex02/include/timer0.h new file mode 100644 index 0000000..099bccd --- /dev/null +++ b/ex02/include/timer0.h @@ -0,0 +1,168 @@ +#ifndef TIMER0_H +# define TIMER0_H + +# include +# include "mystd.h" + +# include "timer_global.h" + +static inline void t0_init_ctc_2(e_timer_prescaler prescaler) { + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR0A = _BV(WGM01); + + // reset to zero -> timer off + TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); + // set the correct prescaler + switch (prescaler) { + case (PRESCALER_1): { + TCCR0B |= (_BV(CS00)); + break; + } + case (PRESCALER_8): { + TCCR0B |= (_BV(CS01)); + break; + } + case (PRESCALER_64): { + TCCR0B |= (_BV(CS01) | _BV(CS00)); + break; + } + case (PRESCALER_256): { + TCCR0B |= (_BV(CS02)); + break; + } + case (PRESCALER_1024): { + TCCR0B |= (_BV(CS02) | _BV(CS00)); + break; + } + case (PRESCALER_OFF): { + break; + } + } +} + +static inline void t0_init_fpwm_3(e_timer_prescaler prescaler) { + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR0A = _BV(WGM00) | _BV(WGM01); + + // reset to zero -> timer off + TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00)); + // set the correct prescaler + switch (prescaler) { + case (PRESCALER_1): { + TCCR0B |= (_BV(CS00)); + break; + } + case (PRESCALER_8): { + TCCR0B |= (_BV(CS01)); + break; + } + case (PRESCALER_64): { + TCCR0B |= (_BV(CS01) | _BV(CS00)); + break; + } + case (PRESCALER_256): { + TCCR0B |= (_BV(CS02)); + break; + } + case (PRESCALER_1024): { + TCCR0B |= (_BV(CS02) | _BV(CS00)); + break; + } + case (PRESCALER_OFF): { + break; + } + } +} + +static inline void t0_overflow_interrupt(bool enable) { + if (enable) + TIMSK0 |= _BV(TOIE0); + else + TIMSK0 &= ~_BV(TOIE0); +} + +static inline void t0_interrupt(enum e_timer_output output, bool enable) { + if (output & TO_A) { + if (enable) + TIMSK0 |= _BV(OCIE0A); + else + TIMSK0 &= ~_BV(OCIE0A); + } + if (output & TO_B) { + if (enable) + TIMSK0 |= _BV(OCIE0B); + else + TIMSK0 &= ~_BV(OCIE0B); + } +} + +static inline void t0_set_ocr(enum e_timer_output output, uint8_t value) { + if (output & TO_A) + OCR0A = value; + if (output & TO_B) + OCR0B = value; +} + +static inline void t0_set_out_mode(enum e_timer_output output, enum e_timer_output_mode mode) { + if (output & TO_A) { + TCCR0A &= ~(_BV(COM0A1) | _BV(COM0A0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR0A |= (_BV(COM0A1)); + break; + } + case (TOM_01): { + TCCR0A |= (_BV(COM0A0)); + break; + } + case (TOM_11): { + TCCR0A |= (_BV(COM0A1) | _BV(COM0A0)); + break; + } + } + } + if (output & TO_B) { + TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR0A |= (_BV(COM0B1)); + break; + } + case (TOM_01): { + TCCR0A |= (_BV(COM0B0)); + break; + } + case (TOM_11): { + TCCR0A |= (_BV(COM0B1) | _BV(COM0B0)); + break; + } + } + } +} + +// OC2B => RED => PD3 +// OC0B => GREEN => PD5 +// OC0A => BLUE => PD6 + +#endif /* TIMER0_H */ + +/* + // OC2B = PD3 → output + DDRD |= _BV(DDD3) | _BV(DDD5) | _BV(DDD6); + + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR0A = _BV(WGM00) | _BV(WGM01); + TCCR0A |= _BV(COM0B1); + + // 50% duty cycle + OCR0B = 128; + + // Start timer, prescaler = 64 + TCCR0B = _BV(CS02); +*/ diff --git a/ex02/include/timer1.h b/ex02/include/timer1.h new file mode 100644 index 0000000..1c61211 --- /dev/null +++ b/ex02/include/timer1.h @@ -0,0 +1,159 @@ +#ifndef TIMER1_H +#define TIMER1_H + +#include +#include "mystd.h" + +#include "timer_global.h" + +static inline void t1_init_fpwm_14(e_timer_prescaler prescaler) { + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR1A = _BV(WGM11) | _BV(WGM12); + TCCR1B = _BV(WGM13); + + // set the correct prescaler + switch (prescaler) { + case (PRESCALER_1): { + TCCR1B |= (_BV(CS10)); + break; + } + case (PRESCALER_8): { + TCCR1B |= (_BV(CS11)); + break; + } + case (PRESCALER_64): { + TCCR1B |= (_BV(CS11) | _BV(CS10)); + break; + } + case (PRESCALER_256): { + TCCR1B |= (_BV(CS12)); + break; + } + case (PRESCALER_1024): { + TCCR1B |= (_BV(CS12) | _BV(CS10)); + break; + } + case (PRESCALER_OFF): { + break; + } + } +} + +static inline void t1_init_ctc_4(e_timer_prescaler prescaler) { + // CTC mode 4 + TCCR1A = _BV(WGM12); + TCCR1B = 0; + + // set the correct prescaler + switch (prescaler) { + case (PRESCALER_1): { + TCCR1B |= (_BV(CS10)); + break; + } + case (PRESCALER_8): { + TCCR1B |= (_BV(CS11)); + break; + } + case (PRESCALER_64): { + TCCR1B |= (_BV(CS11) | _BV(CS10)); + break; + } + case (PRESCALER_256): { + TCCR1B |= (_BV(CS12)); + break; + } + case (PRESCALER_1024): { + TCCR1B |= (_BV(CS12) | _BV(CS10)); + break; + } + case (PRESCALER_OFF): { + break; + } + } +} + +static inline void t1_set_counter(uint16_t val) { + TCNT1 = val; +} + +static inline void t1_set_icr1(uint16_t value) { + ICR1 = value; +} + +static inline void t1_overflow_interrupt(bool enable) { + if (enable) + TIMSK1 |= _BV(TOIE1); + else + TIMSK1 &= ~_BV(TOIE1); +} + +static inline void t1_interrupt(enum e_timer_output output, bool enable) { + if (output & TO_A) { + if (enable) + TIMSK1 |= _BV(OCIE1A); + else + TIMSK1 &= ~_BV(OCIE1A); + } + if (output & TO_B) { + if (enable) + TIMSK1 |= _BV(OCIE1B); + else + TIMSK1 &= ~_BV(OCIE1B); + } +} + +static inline void t1_set_ocr(enum e_timer_output output, uint16_t value) { + if (output & TO_A) + OCR1A = value; + if (output & TO_B) + OCR1B = value; +} + +static inline void t1_set_out_mode(enum e_timer_output output, enum e_timer_output_mode mode) { + if (output & TO_A) { + TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR1A |= (_BV(COM1A1)); + break; + } + case (TOM_01): { + TCCR1A |= (_BV(COM1A0)); + break; + } + case (TOM_11): { + TCCR1A |= (_BV(COM1A1) | _BV(COM1A0)); + break; + } + } + } + if (output & TO_B) { + TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR1A |= (_BV(COM1B1)); + break; + } + case (TOM_01): { + TCCR1A |= (_BV(COM1B0)); + break; + } + case (TOM_11): { + TCCR1A |= (_BV(COM1B1) | _BV(COM1B0)); + break; + } + } + } +} + +// OC2B => RED => PD3 +// OC0B => GREEN => PD5 +// OC0A => BLUE => PD6 + +#endif /* TIMER1_H */ diff --git a/ex02/include/timer2.h b/ex02/include/timer2.h new file mode 100644 index 0000000..b26b41b --- /dev/null +++ b/ex02/include/timer2.h @@ -0,0 +1,134 @@ +#ifndef TIMER2_H +# define TIMER2_H + +# include +# include "mystd.h" + +# include "timer_global.h" + +static inline void t2_init_fpwm_3(e_timer_prescaler prescaler) { + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR2A = _BV(WGM20) | _BV(WGM21); + + // reset to zero -> timer off + TCCR2B &= ~(_BV(CS22) | _BV(CS21) | _BV(CS20)); + // set the correct prescaler + switch (prescaler) { + case (PRESCALER_1): { + TCCR2B |= (_BV(CS20)); + break; + } + case (PRESCALER_8): { + TCCR2B |= (_BV(CS21)); + break; + } + case (PRESCALER_64): { + TCCR2B |= (_BV(CS21) | _BV(CS20)); + break; + } + case (PRESCALER_256): { + TCCR2B |= (_BV(CS22)); + break; + } + case (PRESCALER_1024): { + TCCR2B |= (_BV(CS22) | _BV(CS20)); + break; + } + case (PRESCALER_OFF): { + break; + } + } +} + +static inline void t2_overflow_interrupt(bool enable) { + if (enable) + TIMSK2 |= _BV(TOIE2); + else + TIMSK2 &= ~_BV(TOIE2); +} + +static inline void t2_interrupt(enum e_timer_output output, bool enable) { + if (output & TO_A) { + if (enable) + TIMSK2 |= _BV(OCIE2A); + else + TIMSK2 &= ~_BV(OCIE2A); + } + if (output & TO_B) { + if (enable) + TIMSK2 |= _BV(OCIE2B); + else + TIMSK2 &= ~_BV(OCIE2B); + } +} + +static inline void t2_set_ocr(enum e_timer_output output, uint8_t value) { + if (output & TO_A) + OCR2A = value; + if (output & TO_B) + OCR2B = value; +} + +static inline void t2_set_out_mode(enum e_timer_output output, enum e_timer_output_mode mode) { + if (output & TO_A) { + TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR2A |= (_BV(COM2A1)); + break; + } + case (TOM_01): { + TCCR2A |= (_BV(COM2A0)); + break; + } + case (TOM_11): { + TCCR2A |= (_BV(COM2A1) | _BV(COM2A0)); + break; + } + } + } + if (output & TO_B) { + TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0)); + switch (mode) { + case (TOM_00): { + break; + } + case (TOM_10): { + TCCR2A |= (_BV(COM2B1)); + break; + } + case (TOM_01): { + TCCR2A |= (_BV(COM2B0)); + break; + } + case (TOM_11): { + TCCR2A |= (_BV(COM2B1) | _BV(COM2B0)); + break; + } + } + } +} + +// OC2B => RED => PD3 +// OC0B => GREEN => PD5 +// OC0A => BLUE => PD6 + +#endif /* TIMER0_H */ + +/* + // OC2B = PD3 → output + DDRD |= _BV(DDD3) | _BV(DDD5) | _BV(DDD6); + + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR2A = _BV(GM20) | _BV(GM21); + TCCR2A |= _BV(COM2B1); + + // 50% duty cycle + OCR0B = 128; + + // Start timer, prescaler = 64 + TCCR2B = _BV(CS22); +*/ diff --git a/ex02/include/timer_global.h b/ex02/include/timer_global.h new file mode 100644 index 0000000..7e1833a --- /dev/null +++ b/ex02/include/timer_global.h @@ -0,0 +1,25 @@ +#ifndef TIMER_GLOBAL_H +#define TIMER_GLOBAL_H + +typedef enum e_timer_prescaler { + PRESCALER_OFF = 0, + PRESCALER_1 = 1, + PRESCALER_8 = 8, + PRESCALER_64 = 64, + PRESCALER_256 = 256, + PRESCALER_1024 = 1024, +} e_timer_prescaler; + +typedef enum e_timer_output { + TO_A = (1 << 0), + TO_B = (1 << 1), +} e_timer_output; + +typedef enum e_timer_output_mode { + TOM_00, + TOM_01, + TOM_10, + TOM_11, +} e_timer_output_mode; + +#endif /* TIMER_GLOBAL_H */ diff --git a/ex02/include/uart.h b/ex02/include/uart.h new file mode 100644 index 0000000..0b37020 --- /dev/null +++ b/ex02/include/uart.h @@ -0,0 +1,19 @@ +#ifndef UART_H +#define UART_H + +#include "mystd.h" + +void uart_init(void); + +void uart_tx(char data); +void uart_sendstring(const char* str); + +void uart_send_u8(uint8_t val); +void uart_send_u16(uint16_t val); + +void uart_send_u8_hex(uint8_t val); +void uart_send_u16_hex(uint16_t val); + +char uart_rx(void); + +#endif /* UART_H */ diff --git a/ex02/include/utils.h b/ex02/include/utils.h new file mode 100644 index 0000000..e5badd6 --- /dev/null +++ b/ex02/include/utils.h @@ -0,0 +1,12 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "mystd.h" + +void delay_ms(uint16_t count); + +void ft_bzero(void *data, uint16_t size); + +uint8_t ft_stridx(const char* str, char chr); + +#endif /* UTILS_H */ diff --git a/ex02/src/main.c b/ex02/src/main.c new file mode 100644 index 0000000..af3ae59 --- /dev/null +++ b/ex02/src/main.c @@ -0,0 +1,79 @@ +#include +#include + +#include "interupt.h" +#include "mystd.h" + +#include "timer_global.h" +#include "uart.h" + +#include "timer0.h" +#include "timer1.h" +#include "timer2.h" + +volatile uint8_t counter = 0; + +#define LED_MASK (_BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB4)); + +void display_counter(void) { + PORTB &= ~LED_MASK; + PORTB |= ((counter & 0b111) | ((counter & 0b1000) << 1)); +} + +// interrupt for INT0 (external interrupt) +void __attribute__((signal, used)) __vector_1(void) { + EIMSK &= ~_BV(INT0); + EIFR |= _BV(INTF0); + // clear the register "pending" bit + t1_interrupt(TO_A, true); + counter++; + display_counter(); + // uart_tx('I'); +} + +// external interupt +void __attribute__((signal, used)) __vector_5(void) { + EIMSK &= ~_BV(INT0); + EIFR |= _BV(INTF0); + // clear the register "pending" bit + t1_interrupt(TO_A, true); + counter++; + display_counter(); + // uart_tx('I'); +} + +// timer1 compare match A +void __attribute__((signal, used)) __vector_11(void) { + static uint8_t prev_state = 0; +} + +// timer1 overflow match B + +int main(void) { + uart_init(); + + t1_init_ctc_4(PRESCALER_1024); + t1_interrupt(TO_B, true); + t1_set_ocr(TO_A, (F_CPU / (1024 * 20)) - 1); + + DDRB |= LED_MASK; + DDRD &= ~(_BV(PD2) | _BV(PD4)); + + // Set INT0 to trigger on falling edge + EICRA &= ~(_BV(ISC00) | _BV(ISC01)); + EICRA |= _BV(ISC01); + + // yes we want the INT0 interupt ! + EIMSK |= _BV(INT0); + + // PD4 + // Enable Pin Change Interrupt group for PORTD (PCINT[23:16]) + PCICR |= (1 << PCIE2); + + // Enable interrupt specifically for PD4 (PCINT20) + PCMSK2 |= (1 << PCINT20); + + my_sei(); + while (1) { + } +} diff --git a/ex02/src/uart.c b/ex02/src/uart.c new file mode 100644 index 0000000..805d7b5 --- /dev/null +++ b/ex02/src/uart.c @@ -0,0 +1,103 @@ +#include "uart.h" +#include +#include "mystd.h" +#include "utils.h" + +#define BAUD_RATE 115200 + +#define UBRR_VALUE ((F_CPU / (8UL * BAUD_RATE)) - 1) + +// uart is 115200 baud rate, 8 bits per word, no parrity and 1 stop bit +// 115200 8N1 +void uart_init(void) { + // Set baud rate + UBRR0H = (uint8_t)(UBRR_VALUE >> 8); + UBRR0L = (uint8_t)(UBRR_VALUE); + + UCSR0A |= _BV(U2X0); + // Enable transmitter + UCSR0B = _BV(TXEN0) | _BV(RXEN0); + + // Set frame format: 8 data bits, no parity, 1 stop bit + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); + + // Set TX (PD1) as output + DDRD |= _BV(PD1); +} + +void uart_tx(char data) { + // wait for transmit buffer to be empty + while (!(UCSR0A & _BV(UDRE0))) + ; + // load data into transmit register + UDR0 = data; +} + +char uart_rx(void) { + while (!(UCSR0A & _BV(RXC0))) + ; + return UDR0; +} + +void uart_sendstring(const char* str) { + if (!str) + return; + while (*str) { + uart_tx(*str); + str++; + } +} +void uart_send_u8(uint8_t val) { + char buf[4] = {0, 0, 0, 0}; + uint8_t idx = 0; + bool print = false; + + uint8_t modulus = 100; + while (modulus) { + uint8_t digit = val / modulus; + if (print || digit != 0) { + print = true; + buf[idx++] = '0' + digit; + } + val %= modulus; + modulus /= 10; + } + + uart_sendstring(buf); +} + +void uart_send_u16(uint16_t val) { + char buf[6] = {0, 0, 0, 0, 0, 0}; + uint8_t idx = 0; + bool print = false; + + uint16_t modulus = 10000; + while (modulus) { + uint8_t digit = val / modulus; + if (print || digit != 0) { + print = true; + buf[idx++] = '0' + digit; + } + val %= modulus; + modulus /= 10; + } + uart_sendstring(buf); +} + +void uart_send_u8_hex(uint8_t val) { + char buf[3] = {0, 0, 0}; + buf[0] = "0123456789abcdef"[(val >> 4) & 0x0F]; + buf[1] = "0123456789abcdef"[(val >> 0) & 0x0F]; + + uart_sendstring(buf); +} + +void uart_send_u16_hex(uint16_t val) { + char buf[5] = {0, 0, 0, 0, 0}; + + buf[0] = "0123456789abcdef"[(val >> 12) & 0x0F]; + buf[1] = "0123456789abcdef"[(val >> 8) & 0x0F]; + buf[2] = "0123456789abcdef"[(val >> 4) & 0x0F]; + buf[3] = "0123456789abcdef"[(val >> 0) & 0x0F]; + uart_sendstring(buf); +} diff --git a/ex02/src/utils.c b/ex02/src/utils.c new file mode 100644 index 0000000..7df766d --- /dev/null +++ b/ex02/src/utils.c @@ -0,0 +1,39 @@ +#include "utils.h" +#include "mystd.h" + +// this just burns cycles. +// the volatile is important, it means that the cpu can't optimize any +// read/writes for the value +static inline void spin_loop(volatile uint16_t counts) { + while (counts) + counts--; +} + +void delay_ms(uint16_t ms) { + while (ms) { + // this value was taken using a delay of 500ms, and just recording the led + // blinking. it seems to be high enough such that each loop of delay_loop + // takes 1ms :D + spin_loop((F_CPU) / 5000); + ms--; + } +} + +void ft_bzero(void* data, uint16_t size) { + char* d = data; + while (size) { + *d = 0; + d++; + size--; + } +} + +uint8_t ft_stridx(const char* str, char chr) { + if (!str) + return -1; + for (uint8_t i = 0; str[i]; i++) { + if (str[i] == chr) + return i; + } + return -1; +}