From dcf95168e141944f8e7c48611881e21a5288b11d Mon Sep 17 00:00:00 2001 From: Maix0 <39835848+Maix0@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:46:08 +0200 Subject: [PATCH] feat(ex06) --- ex06/Makefile | 75 ++++++++++++++ ex06/include/lib/adc.h | 74 ++++++++++++++ ex06/include/lib/aht20.h | 19 ++++ ex06/include/lib/apa102.h | 47 +++++++++ ex06/include/lib/eeprom.h | 70 +++++++++++++ ex06/include/lib/i2c.h | 20 ++++ ex06/include/lib/interupt.h | 15 +++ ex06/include/lib/milis.h | 23 +++++ ex06/include/lib/mystd.h | 27 +++++ ex06/include/lib/pca9555.h | 23 +++++ ex06/include/lib/pcf8563.h | 24 +++++ ex06/include/lib/rgb.h | 9 ++ ex06/include/lib/segment.h | 51 ++++++++++ ex06/include/lib/spi.h | 16 +++ ex06/include/lib/timer0.h | 168 ++++++++++++++++++++++++++++++++ ex06/include/lib/timer1.h | 159 ++++++++++++++++++++++++++++++ ex06/include/lib/timer2.h | 134 +++++++++++++++++++++++++ ex06/include/lib/timer_global.h | 25 +++++ ex06/include/lib/uart.h | 29 ++++++ ex06/include/lib/utils.h | 27 +++++ ex06/src/lib/aht20.c | 87 +++++++++++++++++ ex06/src/lib/i2c.c | 64 ++++++++++++ ex06/src/lib/milis.c | 36 +++++++ ex06/src/lib/pca9555.c | 52 ++++++++++ ex06/src/lib/pcf8563.c | 87 +++++++++++++++++ ex06/src/lib/rgb.c | 45 +++++++++ ex06/src/lib/segment.c | 54 ++++++++++ ex06/src/lib/spi.c | 31 ++++++ ex06/src/lib/uart.c | 139 ++++++++++++++++++++++++++ ex06/src/lib/utils.c | 125 ++++++++++++++++++++++++ ex06/src/main.c | 51 ++++++++++ 31 files changed, 1806 insertions(+) create mode 100644 ex06/Makefile create mode 100644 ex06/include/lib/adc.h create mode 100644 ex06/include/lib/aht20.h create mode 100644 ex06/include/lib/apa102.h create mode 100644 ex06/include/lib/eeprom.h create mode 100644 ex06/include/lib/i2c.h create mode 100644 ex06/include/lib/interupt.h create mode 100644 ex06/include/lib/milis.h create mode 100644 ex06/include/lib/mystd.h create mode 100644 ex06/include/lib/pca9555.h create mode 100644 ex06/include/lib/pcf8563.h create mode 100644 ex06/include/lib/rgb.h create mode 100644 ex06/include/lib/segment.h create mode 100644 ex06/include/lib/spi.h create mode 100644 ex06/include/lib/timer0.h create mode 100644 ex06/include/lib/timer1.h create mode 100644 ex06/include/lib/timer2.h create mode 100644 ex06/include/lib/timer_global.h create mode 100644 ex06/include/lib/uart.h create mode 100644 ex06/include/lib/utils.h create mode 100644 ex06/src/lib/aht20.c create mode 100644 ex06/src/lib/i2c.c create mode 100644 ex06/src/lib/milis.c create mode 100644 ex06/src/lib/pca9555.c create mode 100644 ex06/src/lib/pcf8563.c create mode 100644 ex06/src/lib/rgb.c create mode 100644 ex06/src/lib/segment.c create mode 100644 ex06/src/lib/spi.c create mode 100644 ex06/src/lib/uart.c create mode 100644 ex06/src/lib/utils.c create mode 100644 ex06/src/main.c diff --git a/ex06/Makefile b/ex06/Makefile new file mode 100644 index 0000000..645efe7 --- /dev/null +++ b/ex06/Makefile @@ -0,0 +1,75 @@ +# 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) -MMD -Iinclude +IFLAGS= +LDFLAGS=-Wl,--gc-sections +TARGET=main +SERIAL=-P /dev/ttyUSB0 -b 115200 + +SRC_DIR=src +OBJ_DIR=build + +LIB_FILES= \ + aht20.c \ + i2c.c \ + milis.c \ + pca9555.c \ + pcf8563.c \ + rgb.c \ + spi.c \ + uart.c \ + utils.c \ + segment.c \ + + +SRC_FILES=main.c \ + $(addprefix lib/,$(LIB_FILES)) +OBJ_FILES=$(patsubst %.c,%.o,$(SRC_FILES)) +DEP_FILES=$(patsubst %.c,%.d,$(SRC_FILES)) + +SRC=$(addprefix $(SRC_DIR)/,$(SRC_FILES)) +OBJ=$(addprefix $(OBJ_DIR)/,$(OBJ_FILES)) +DEP=$(addprefix $(OBJ_DIR)/,$(DEP_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) + +-include $(DEP); + diff --git a/ex06/include/lib/adc.h b/ex06/include/lib/adc.h new file mode 100644 index 0000000..d5488fb --- /dev/null +++ b/ex06/include/lib/adc.h @@ -0,0 +1,74 @@ +#ifndef ADC_H +#define ADC_H + +#include +#include "lib/mystd.h" + +typedef enum e_adc_ref { + ADC_AREF = 0, + ADC_AVCC = BV(REFS0), + ADC_INT = BV(REFS0) | BV(REFS1), + + _ADC_REF_MASK = BV(REFS0) | BV(REFS1), +} e_adc_ref; + +typedef enum e_adc_size { + ADC_10BIT = 0, + ADC_8BIT = BV(ADLAR), + _ADC_SIZE_MASK = BV(ADLAR), +} e_adc_size; + +typedef enum e_adc_prescaler { + ADC_PRESCALER_2 = BV(ADPS0), + ADC_PRESCALER_4 = BV(ADPS1), + ADC_PRESCALER_8 = BV(ADPS1) | BV(ADPS0), + ADC_PRESCALER_16 = BV(ADPS2), + ADC_PRESCALER_32 = BV(ADPS2) | BV(ADPS0), + ADC_PRESCALER_64 = BV(ADPS2) | BV(ADPS1), + ADC_PRESCALER_128 = BV(ADPS2) | BV(ADPS1) | BV(ADPS0), + + _ADC_PRESCALER_MASK = BV(ADPS2) | BV(ADPS1) | BV(ADPS0), + +} e_adc_prescaler; + +typedef enum e_adc_input { + ADC_ADC0 = 0, + ADC_ADC1 = BV(MUX0), + ADC_ADC2 = BV(MUX1), + ADC_ADC3 = BV(MUX0) | BV(MUX1), + ADC_ADC4 = BV(MUX2), + ADC_ADC5 = BV(MUX2) | BV(MUX0), + ADC_ADC6 = BV(MUX2) | BV(MUX1), + ADC_ADC7 = BV(MUX2) | BV(MUX1) | BV(MUX0), + ADC_TEMP = BV(MUX3), + ADC_11V = BV(MUX3) | BV(MUX2) | BV(MUX1), + ADC_0V = BV(MUX3) | BV(MUX2) | BV(MUX1) | BV(MUX0), + _ADC_INPUT_MASK = BV(MUX0) | BV(MUX1) | BV(MUX2) | BV(MUX3), +} e_adc_input; + +static inline uint8_t adc_read_pin(e_adc_input input) { + ADMUX = (ADMUX & ~(_ADC_INPUT_MASK | _ADC_SIZE_MASK)) | input | BV(ADLAR); + ADCSRA |= BV(ADSC); + + while (ADCSRA & BV(ADSC)) + ; + return ADCH; +} + +static inline uint16_t adc_read_pin_10bit(e_adc_input input) { + ADMUX = (ADMUX & ~(_ADC_SIZE_MASK | _ADC_INPUT_MASK)) | (input & _ADC_INPUT_MASK); + ADCSRA |= BV(ADSC); + + while (ADCSRA & BV(ADSC)) + ; + return ADC; +} + +static inline void adc_init(e_adc_ref v_ref, e_adc_prescaler prescaler) { + ADMUX = (ADMUX & ~(_ADC_REF_MASK)) | v_ref; + + ADCSRA = BV(ADEN) | (prescaler & _ADC_PRESCALER_MASK); + + (void)adc_read_pin(0); +} +#endif /* ADC_H */ diff --git a/ex06/include/lib/aht20.h b/ex06/include/lib/aht20.h new file mode 100644 index 0000000..f94201e --- /dev/null +++ b/ex06/include/lib/aht20.h @@ -0,0 +1,19 @@ +#ifndef AHT20_H +#define AHT20_H + +#include +#include "lib/mystd.h" + +typedef struct aht20_reading { + float temperature; + float humidity; +} aht20_reading; + +t_error aht20_read_measure(aht20_reading* out); +t_error aht20_status(uint8_t* out); +t_error aht20_init(void); +t_error aht20_trigger(void); +void aht20_print_status(uint8_t status); +#define AHT20 (0x38) + +#endif /* AHT20_H */ diff --git a/ex06/include/lib/apa102.h b/ex06/include/lib/apa102.h new file mode 100644 index 0000000..3574032 --- /dev/null +++ b/ex06/include/lib/apa102.h @@ -0,0 +1,47 @@ +#ifndef APA102_H +#define APA102_H + +#include +#include "lib/mystd.h" +#include "lib/spi.h" + +typedef struct rgb { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb; + +static inline void apa102_set_colors(rgb col[], uint8_t brightness[], uint8_t length) { + // start frame + for (uint8_t i = 0; i < 4; i++) + spi_write(0x00); + // led data + for (uint8_t i = 0; i < length; i++) { + spi_write(0xE0 + (brightness[i] & 31)); + spi_write(col[i].b); + spi_write(col[i].g); + spi_write(col[i].r); + } + + // reset frame + for (uint8_t i = 0; i < 4; i++) + spi_write(0xFF); + + // End frame: 8+8*(leds >> 4) clock cycles + for (uint8_t i = 0; i < length; i += 16) { + spi_write(0xFF); // 8 more clock cycles + } +} + +static inline void apa102_set_color(rgb col, uint8_t brightness) { + apa102_set_colors(&col, &brightness, 1); +} + +static inline void apa102_set_all_color(rgb col) +{ + rgb cols[3] = {col, col, col}; + uint8_t brightness[3] = {31, 31, 31}; + apa102_set_colors(cols, brightness, 3); +} + +#endif /* APA102_H */ diff --git a/ex06/include/lib/eeprom.h b/ex06/include/lib/eeprom.h new file mode 100644 index 0000000..56089c5 --- /dev/null +++ b/ex06/include/lib/eeprom.h @@ -0,0 +1,70 @@ +#ifndef EEPROM_H +#define EEPROM_H + +#include +#include "lib/interupt.h" +#include "lib/mystd.h" + +static inline void eeprom_wait(void) { + while (EECR & BV(EEPE)) + ; +} +static inline void eeprom_write_single_nocheck(uint16_t addr, uint8_t data) { + uint8_t sreg_save = SREG; + my_cli(); + + // wait until eeprom is good to go ! + eeprom_wait(); + while (SPMCSR & BV(SPMEN)) + ; + + // setup the (addr, data) pair + EEAR = addr; + EEDR = data; + + // we set the EEPROM master write bit + EECR = (EECR & ~BV(EEPE)) | BV(EEMPE); + // within four cpu cycle, we need to set the EEPROM write bit, otherwise the writes doesnt work + // why 4 ? -> the master bit is reset after 4 cycles, and you need both to actually write + EECR |= BV(EEPE); + + // this is the exact setup written in p32 + // here the write should be done + SREG = sreg_save; +} + +static inline uint8_t eeprom_read_single(uint16_t addr) { + uint8_t sreg_save = SREG; + my_cli(); + eeprom_wait(); + + EEAR = addr; + + EECR |= BV(EERE); + + SREG = sreg_save; + return EEDR; +} + +// true on error +static inline bool eeprom_write_single(uint16_t addr, uint8_t data) { + if (eeprom_read_single(addr) != data) + eeprom_write_single_nocheck(addr, data); + + return (eeprom_read_single(addr) != data); +} +// true on error +static inline bool eeprom_write(uint16_t addr, const uint8_t* data, uint16_t length) { + for (uint16_t i = 0; i < length; i++) { + if (eeprom_write_single(addr + i, data[i])) + return true; + } + return false; +} + +static inline void eeprom_read(uint16_t addr, uint8_t* data, uint16_t length) { + for (uint16_t i = 0; i < length; i++) + data[i] = eeprom_read_single(addr + i); +} + +#endif /* EEPROM_H */ diff --git a/ex06/include/lib/i2c.h b/ex06/include/lib/i2c.h new file mode 100644 index 0000000..21a95c3 --- /dev/null +++ b/ex06/include/lib/i2c.h @@ -0,0 +1,20 @@ +#ifndef I2C_H +#define I2C_H + +#include +#include + +#include "lib/mystd.h" + +#define I2C_CLOCK 400000UL + +#define I2C_ADDR(ADDR, RW) ((ADDR) << 1 | (RW & 1)) + +void i2c_init(void); +t_error i2c_start(void); +t_error i2c_stop(void); +t_error i2c_write(uint8_t); +t_error i2c_read_nack(uint8_t* out); +t_error i2c_read_ack(uint8_t* out); + +#endif /* I2C_H */ diff --git a/ex06/include/lib/interupt.h b/ex06/include/lib/interupt.h new file mode 100644 index 0000000..5b9c445 --- /dev/null +++ b/ex06/include/lib/interupt.h @@ -0,0 +1,15 @@ +#ifndef INTERUPT_H +#define INTERUPT_H + +#include +#include "lib/mystd.h" + +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/ex06/include/lib/milis.h b/ex06/include/lib/milis.h new file mode 100644 index 0000000..1e42637 --- /dev/null +++ b/ex06/include/lib/milis.h @@ -0,0 +1,23 @@ +#ifndef MILIS_H +#define MILIS_H + +#include "lib/mystd.h" + +typedef uint32_t milis_t; + +typedef struct { + milis_t until; +} timer_t; + +milis_t milis(void); +void milis_init(void); + +// return true if the timer is past the time set. +// YOU NEED TO RESET THE TIMER OTHERWISE IT WILL KEEP BEING PENDING +bool timer_wait(timer_t* timer, milis_t ms); + +static inline timer_t timer_reset(void) { + return (timer_t){.until = 0}; +} + +#endif /* MILIS_H */ diff --git a/ex06/include/lib/mystd.h b/ex06/include/lib/mystd.h new file mode 100644 index 0000000..dac6075 --- /dev/null +++ b/ex06/include/lib/mystd.h @@ -0,0 +1,27 @@ +#ifndef MYSTDINT_H +#define MYSTDINT_H + +#include + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef uint8_t bool; + +#define true (1) +#define false (0) + +#define BV(bit) (1 << bit) + +#ifndef NULL +# define NULL ((void*)0) +#endif + +typedef bool t_error; + +#define ERROR true +#define NO_ERROR false + +#define ERR_RET(expr) if (expr) return ERROR + +#endif /* MYSTDINT_H */ diff --git a/ex06/include/lib/pca9555.h b/ex06/include/lib/pca9555.h new file mode 100644 index 0000000..77a37a5 --- /dev/null +++ b/ex06/include/lib/pca9555.h @@ -0,0 +1,23 @@ +#ifndef PCA9555_H +#define PCA9555_H + +#include "lib/mystd.h" + +#define PCA9555_ADDR 0b0100000 +#define PCA9555_INPUT_PORT0 0x00 +#define PCA9555_INPUT_PORT1 0x01 +#define PCA9555_OUTPUT_PORT0 0x02 +#define PCA9555_OUTPUT_PORT1 0x03 +#define PCA9555_CONFIG_PORT0 0x06 +#define PCA9555_CONFIG_PORT1 0x07 + +extern uint8_t ADDR_BITS; +static inline uint8_t pca9555_addr(void) { + return PCA9555_ADDR | ADDR_BITS; +} +t_error pca9555_detect_addr(void); +t_error pca9555_read(uint8_t reg, uint8_t* out); +t_error pca9555_write(uint8_t reg, uint8_t value); +t_error pca9555_write_masked(uint8_t reg, uint8_t value, uint8_t mask); + +#endif /* PCA9555_H */ diff --git a/ex06/include/lib/pcf8563.h b/ex06/include/lib/pcf8563.h new file mode 100644 index 0000000..e2c6d95 --- /dev/null +++ b/ex06/include/lib/pcf8563.h @@ -0,0 +1,24 @@ +#ifndef PCF8563_H +#define PCF8563_H + +#include "lib/i2c.h" +#include "lib/mystd.h" + +#define PCF8563address (0x51) + +typedef struct pcf8563_date { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t dayOfWeek; + uint8_t dayOfMonth; + uint8_t month; + uint16_t year; + +} pcf8563_date; + +t_error pcf8563_read_date(pcf8563_date* out); +t_error pcf8563_set_date(pcf8563_date date); +void print_date(pcf8563_date* date); + +#endif /* PCF8563_H */ diff --git a/ex06/include/lib/rgb.h b/ex06/include/lib/rgb.h new file mode 100644 index 0000000..9f2bcba --- /dev/null +++ b/ex06/include/lib/rgb.h @@ -0,0 +1,9 @@ +#ifndef RGB_H +#define RGB_H + +#include "lib/mystd.h" + +void init_rgb(void); +void set_rgb(uint8_t r, uint8_t g, uint8_t b); + +#endif /* RGB_H */ diff --git a/ex06/include/lib/segment.h b/ex06/include/lib/segment.h new file mode 100644 index 0000000..f5350a5 --- /dev/null +++ b/ex06/include/lib/segment.h @@ -0,0 +1,51 @@ +#ifndef SEGMENT_H +#define SEGMENT_H + +#include "lib/mystd.h" + +typedef uint8_t segment_digit; + +typedef enum { + DIGIT_NONE = 0, + DIGIT_1 = BV((0 + 4)), + DIGIT_2 = BV((1 + 4)), + DIGIT_3 = BV((2 + 4)), + DIGIT_4 = BV((3 + 4)), + DIGIT_ALL = DIGIT_1 | DIGIT_2 | DIGIT_3 | DIGIT_4, + _DIGIT_MASK = DIGIT_ALL, +} segment_mask; + +typedef struct { + segment_digit digits[4]; + segment_mask mask; + bool enable; +} segment_data; + +#define SEG_0 (0b00111111) +#define SEG_1 (0b00000110) +#define SEG_2 (0b01011011) +#define SEG_3 (0b01001111) +#define SEG_4 (0b01100110) +#define SEG_5 (0b01101101) +#define SEG_6 (0b01111101) +#define SEG_7 (0b00000111) +#define SEG_8 (0b01111111) +#define SEG_9 (0b01101111) + +#define SEG_a (BV(0)) +#define SEG_b (BV(1)) +#define SEG_c (BV(2)) +#define SEG_d (BV(3)) +#define SEG_e (BV(4)) +#define SEG_f (BV(5)) +#define SEG_g (BV(6)) +#define SEG_dp (BV(7)) + +t_error segment_init(void); +t_error segment_set_digit(segment_digit data, segment_mask digit); +t_error segment_set_mask(segment_mask mask); +t_error segment_refresh_display(void); +void segment_enable(void); +void segment_disable(void); + +#endif /* SEGMENT_H */ diff --git a/ex06/include/lib/spi.h b/ex06/include/lib/spi.h new file mode 100644 index 0000000..ce32f9f --- /dev/null +++ b/ex06/include/lib/spi.h @@ -0,0 +1,16 @@ +#ifndef SPI_H +#define SPI_H + +#define SPI_DDR DDRB +#define CS PINB2 +#define MOSI PINB3 +#define MISO PINB4 +#define SCK PINB5 + +#include "lib/mystd.h" + +void spi_init(void); +void spi_uninit(void); +t_error spi_write(uint8_t data); + +#endif /* SPI_H */ diff --git a/ex06/include/lib/timer0.h b/ex06/include/lib/timer0.h new file mode 100644 index 0000000..e7f6e92 --- /dev/null +++ b/ex06/include/lib/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/ex06/include/lib/timer1.h b/ex06/include/lib/timer1.h new file mode 100644 index 0000000..9bfa729 --- /dev/null +++ b/ex06/include/lib/timer1.h @@ -0,0 +1,159 @@ +#ifndef TIMER1_H +#define TIMER1_H + +#include +#include "lib/mystd.h" + +#include "lib/timer_global.h" + +static inline void t1_init_fpwm_14(e_timer_prescaler prescaler) { + // Fast PWM (8-bit): WGM22:0 = 0b011 + TCCR1A = BV(WGM11); + TCCR1B = BV(WGM12) | 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 = 0; + TCCR1B = BV(WGM12); + + // 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/ex06/include/lib/timer2.h b/ex06/include/lib/timer2.h new file mode 100644 index 0000000..30bf320 --- /dev/null +++ b/ex06/include/lib/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/ex06/include/lib/timer_global.h b/ex06/include/lib/timer_global.h new file mode 100644 index 0000000..7e1833a --- /dev/null +++ b/ex06/include/lib/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/ex06/include/lib/uart.h b/ex06/include/lib/uart.h new file mode 100644 index 0000000..1ac29b4 --- /dev/null +++ b/ex06/include/lib/uart.h @@ -0,0 +1,29 @@ +#ifndef UART_H +#define UART_H + +#include "lib/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_u32(uint32_t val); + + +void uart_send_i16(int16_t val); + +void uart_send_u8_hex(uint8_t val); +void uart_send_u16_hex(uint16_t val); + + +static inline void print_hex_value(char c) +{ + uart_send_u8_hex(c); +} + +char uart_rx(void); + +#endif /* UART_H */ diff --git a/ex06/include/lib/utils.h b/ex06/include/lib/utils.h new file mode 100644 index 0000000..0fe4059 --- /dev/null +++ b/ex06/include/lib/utils.h @@ -0,0 +1,27 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "lib/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); +uint8_t ft_ftoa(float val, char* out, uint16_t precision); + +void* ft_memcpy(void* dest, void* src, uint16_t len); +bool ft_memcmp(void* s1, void* s2, uint16_t len); + +// return -1 on not hex digit +uint8_t is_hex_digit(char chr); + +static inline uint8_t bcdToDec(uint8_t value) { + return ((value / 16) * 10 + value % 16); +} + +static inline uint8_t decToBcd(uint8_t value) { + return (value / 10 * 16 + value % 10); +} + +#endif /* UTILS_H */ diff --git a/ex06/src/lib/aht20.c b/ex06/src/lib/aht20.c new file mode 100644 index 0000000..3cac82a --- /dev/null +++ b/ex06/src/lib/aht20.c @@ -0,0 +1,87 @@ +#include +#include + +#include "lib/aht20.h" +#include "lib/i2c.h" +#include "lib/uart.h" + +void aht20_print_status(uint8_t status) { + uart_sendstring("AHT20: "); + uart_sendstring((status & BV(3)) ? "Calibrated" : "Not Calibrated"); + uart_sendstring(";"); + uart_sendstring((status & BV(7)) ? "Busy" : "Not Busy"); + uart_sendstring(";\r\n"); +} + +// https://datasheet4u.com/pdf/1551700/AHT20.pdf + +t_error aht20_init(void) { + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(AHT20, TW_WRITE))); + ERR_RET(i2c_write(0xBE)); + ERR_RET(i2c_stop()); + + return NO_ERROR; +} + +t_error aht20_trigger(void) { + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(AHT20, TW_WRITE))); + // trigger measurement command (7.4) + ERR_RET(i2c_write(0xAC)); + ERR_RET(i2c_write(0x33)); + ERR_RET(i2c_write(0x00)); + ERR_RET(i2c_stop()); + + return NO_ERROR; +} + +t_error aht20_read_measure(aht20_reading* out) { + uint8_t data[6]; + + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(AHT20, TW_READ))); + + for (uint8_t i = 0; i < 5; i++) + ERR_RET(i2c_read_ack(&data[i])); + + ERR_RET(i2c_read_nack(&data[5])); + // we dont read the checksum + + i2c_stop(); + + uint32_t raw_humi = 0; + uint32_t raw_temp = 0; + + struct aht20_reading val; + + raw_humi = data[1]; + raw_humi <<= 8; + raw_humi += data[2]; + raw_humi <<= 4; + raw_humi += data[3] >> 4; + + val.humidity = (float)raw_humi / 1048576.0; + + raw_temp = data[3] & 0x0f; + raw_temp <<= 8; + raw_temp += data[4]; + raw_temp <<= 8; + raw_temp += data[5]; + + val.temperature = (float)raw_temp / 1048576.0 * 200.0 - 50.0; + + *out = val; + return NO_ERROR; +} + +t_error aht20_status(uint8_t* out) { + uint8_t status; + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(AHT20, TW_READ))); + ERR_RET(i2c_read_nack(&status)); + ERR_RET(i2c_stop()); + + *out = status & (BV(7) | BV(3)); + return NO_ERROR; +} diff --git a/ex06/src/lib/i2c.c b/ex06/src/lib/i2c.c new file mode 100644 index 0000000..5c61766 --- /dev/null +++ b/ex06/src/lib/i2c.c @@ -0,0 +1,64 @@ +#include "lib/mystd.h" + +#include +#include + +#include "lib/i2c.h" + +static inline t_error i2c_wait(void) { + uint16_t o = 0; + // wait until done + while (!(TWCR & BV(TWINT)) && o < 32768) + o++; + if (o >= 32768) + return ERROR; + return NO_ERROR; +} + +void i2c_init(void) { + // clear the status registers + TWSR = 0x00; + // set the i2c clock speed + TWBR = ((F_CPU / I2C_CLOCK) - 16) / 2; +} + +t_error i2c_start(void) { + // CLR INT | SET MASTER | ENABLE ; + TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN); + ERR_RET(i2c_wait()); + + return NO_ERROR; +} + +t_error i2c_stop(void) { + // CLR INT | ENABLE | STOP ; + TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO); + + return NO_ERROR; +} + +t_error i2c_write(uint8_t data) { + // set data to write + TWDR = data; + // CLR INT | ENABLE ; + TWCR = BV(TWINT) | BV(TWEN); + ERR_RET(i2c_wait()); + + return NO_ERROR; +} + +t_error i2c_read_ack(uint8_t* out) { + TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA); + ERR_RET(i2c_wait()); + + *out = TWDR; + return NO_ERROR; +} + +t_error i2c_read_nack(uint8_t* out) { + TWCR = BV(TWINT) | BV(TWEN); + ERR_RET(i2c_wait()); + + *out = TWDR; + return NO_ERROR; +} diff --git a/ex06/src/lib/milis.c b/ex06/src/lib/milis.c new file mode 100644 index 0000000..13c0f75 --- /dev/null +++ b/ex06/src/lib/milis.c @@ -0,0 +1,36 @@ +#include +#include + +#include "lib/interupt.h" +#include "lib/milis.h" +#include "lib/mystd.h" +#include "lib/timer1.h" +#include "lib/timer_global.h" + +static volatile milis_t TIMER_MS = 0; + +void __attribute__((signal, used)) TIMER1_COMPA_vect(void) { + TIMER_MS++; +} + +// this function will setup timer1 to increment the global variable TIMER_MS every miliseconds +// this function will enable interupts +void milis_init(void) { + my_sei(); + t1_init_ctc_4(PRESCALER_1024); + t1_set_out_mode(TO_A, TOM_00); // do not touch ORC1A + t1_set_ocr(TO_A, (F_CPU / PRESCALER_1024 / 1000) - 1); + t1_interrupt(TO_A, true); +} + +milis_t milis(void) { + return TIMER_MS; +} + +bool timer_wait(timer_t* timer, milis_t ms) { + if (timer->until == 0) { + timer->until = milis() + ms; + } + + return milis() > timer->until; +} diff --git a/ex06/src/lib/pca9555.c b/ex06/src/lib/pca9555.c new file mode 100644 index 0000000..d3bcd6e --- /dev/null +++ b/ex06/src/lib/pca9555.c @@ -0,0 +1,52 @@ +#include "lib/pca9555.h" +#include "lib/i2c.h" +#include "lib/mystd.h" + +uint8_t ADDR_BITS = 0; + +t_error pca9555_detect_addr(void) { + uint8_t val; + for (uint8_t i = 0; i < 8; i++) { + ADDR_BITS = i; + if (!pca9555_read(PCA9555_OUTPUT_PORT0, &val)) { + return NO_ERROR; + } + } + return ERROR; +} + +t_error pca9555_write(uint8_t reg, uint8_t value) { + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(pca9555_addr(), TW_WRITE))); // write mode + ERR_RET(i2c_write(reg)); + ERR_RET(i2c_write(value)); + ERR_RET(i2c_stop()); + + return NO_ERROR; +} + +t_error pca9555_read(uint8_t reg, uint8_t* out) { + uint8_t val; + + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(pca9555_addr(), TW_WRITE))); + ERR_RET(i2c_write(reg)); + + ERR_RET(i2c_start()); // repeated start + ERR_RET(i2c_write(I2C_ADDR(pca9555_addr(), TW_READ))); + ERR_RET(i2c_read_nack(&val)); + + ERR_RET(i2c_stop()); + + *out = val; + return NO_ERROR; +} + +t_error pca9555_write_masked(uint8_t reg, uint8_t value, uint8_t mask) { + uint8_t val; + + ERR_RET(pca9555_read(reg, &val)); + ERR_RET(pca9555_write(reg, (val & ~mask) | (value & mask))); + + return NO_ERROR; +} diff --git a/ex06/src/lib/pcf8563.c b/ex06/src/lib/pcf8563.c new file mode 100644 index 0000000..d66796a --- /dev/null +++ b/ex06/src/lib/pcf8563.c @@ -0,0 +1,87 @@ +// https://tronixstuff.com/2013/08/13/tutorial-arduino-and-pcf8563-real-time-clock-ic/ +// https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf + +#include "lib/pcf8563.h" +#include "lib/i2c.h" +#include "lib/uart.h" +#include "lib/utils.h" + +t_error pcf8563_read_date(pcf8563_date* out) { + uint8_t bytes[7] = {0}; + pcf8563_date ret; + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(PCF8563address, TW_WRITE))); + ERR_RET(i2c_write(0x2)); + ERR_RET(i2c_stop()); + + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(PCF8563address, TW_READ))); + + for (uint8_t i = 0; i < 6; i++) + ERR_RET(i2c_read_ack(&bytes[i])); + ERR_RET(i2c_read_nack(&bytes[6])); + + ret.second = bcdToDec(bytes[0] & 0b01111111); + ret.minute = bcdToDec(bytes[1] & 0b01111111); + ret.hour = bcdToDec(bytes[2] & 0b00111111); + ret.dayOfMonth = bcdToDec(bytes[3] & 0b00111111); + ret.dayOfWeek = bcdToDec(bytes[4] & 0b00000111); + ret.month = bcdToDec(bytes[5] & 0b00011111); + ret.year = bcdToDec(bytes[6]) + ((bytes[5] & 0b10000000) ? 1900 : 2000); + + *out = ret; + return NO_ERROR; +} + +t_error pcf8563_set_date(pcf8563_date date) { + ERR_RET(i2c_start()); + ERR_RET(i2c_write(I2C_ADDR(PCF8563address, TW_WRITE))); + ERR_RET(i2c_write(0x2)); + ERR_RET(i2c_write(decToBcd(date.second))); + ERR_RET(i2c_write(decToBcd(date.minute))); + ERR_RET(i2c_write(decToBcd(date.hour))); + ERR_RET(i2c_write(decToBcd(date.dayOfMonth))); + ERR_RET(i2c_write(decToBcd(date.dayOfWeek))); + uint8_t month = decToBcd(date.month); + + if (date.year < 2000) + month |= 0b10000000; // set century bit for 1900s + else + month &= 0b01111111; // clear for 2000s + + ERR_RET(i2c_write(month)); + ERR_RET(i2c_write(decToBcd(date.year % 100)) /*- ((date.year > 1999) ? 2000 : 1900)))*/); + ERR_RET(i2c_stop()); + return NO_ERROR; +} + +void pcf8563_print_date(pcf8563_date* date) { + if (date->dayOfMonth < 10) + uart_tx('0'); + uart_send_u8(date->dayOfMonth); + + uart_tx('/'); + + if (date->month < 10) + uart_tx('0'); + uart_send_u8(date->month); + + uart_tx('/'); + uart_send_u16(date->year); + + uart_tx(' '); + if (date->hour < 10) + uart_tx('0'); + uart_send_u8(date->hour); + uart_tx(':'); + + if (date->minute < 10) + uart_tx('0'); + uart_send_u8(date->minute); + uart_tx(':'); + if (date->second < 10) + uart_tx('0'); + uart_send_u8(date->second); + + uart_sendstring("\r\n"); +} diff --git a/ex06/src/lib/rgb.c b/ex06/src/lib/rgb.c new file mode 100644 index 0000000..9fa5aa6 --- /dev/null +++ b/ex06/src/lib/rgb.c @@ -0,0 +1,45 @@ +#include +#include "lib/mystd.h" +#include "lib/timer0.h" +#include "lib/timer2.h" + +#define D5_R PD5 +#define D5_G PD6 +#define D5_B PD3 + +#define RGB_MASK (BV(D5_R) | BV(D5_G) | BV(D5_B)) + +void init_rgb(void) { + t0_init_fpwm_3(PRESCALER_64); + t2_init_fpwm_3(PRESCALER_64); + + t0_set_out_mode(TO_A | TO_B, TOM_00); + t2_set_out_mode(TO_B, TOM_00); + DDRD |= RGB_MASK; +} + +void set_rgb(uint8_t r, uint8_t g, uint8_t b) { + if (r == 0x00) { + t0_set_out_mode(TO_B, TOM_00); + PORTD = (PORTD & ~BV(D5_R)); + } else { + t0_set_out_mode(TO_B, TOM_10); + t0_set_ocr(TO_B, r); + } + + if (g == 0x00) { + t0_set_out_mode(TO_A, TOM_00); + PORTD = (PORTD & ~BV(D5_G)); + } else { + t0_set_out_mode(TO_A, TOM_10); + t0_set_ocr(TO_A, g); + } + + if (b == 0x00) { + t2_set_out_mode(TO_B, TOM_00); + PORTD = (PORTD & ~BV(D5_B)); + } else { + t2_set_out_mode(TO_B, TOM_10); + t2_set_ocr(TO_B, b); + } +} diff --git a/ex06/src/lib/segment.c b/ex06/src/lib/segment.c new file mode 100644 index 0000000..ca644b6 --- /dev/null +++ b/ex06/src/lib/segment.c @@ -0,0 +1,54 @@ +#include "lib/segment.h" +#include "lib/mystd.h" +#include "lib/pca9555.h" +#include "lib/timer2.h" +#include "lib/uart.h" + +static segment_data SEGMENT_DATA = {}; + +t_error segment_init(void) { + ERR_RET(pca9555_detect_addr()); + ERR_RET(pca9555_write_masked(PCA9555_CONFIG_PORT0, 0b00000000, 0b11110000)); + ERR_RET(pca9555_write_masked(PCA9555_CONFIG_PORT1, 0x00, 0xFF)); + + return NO_ERROR; +} +t_error segment_set_digit(segment_digit data, segment_mask digit) { + if (digit & DIGIT_1) + SEGMENT_DATA.digits[0] = data; + if (digit & DIGIT_2) + SEGMENT_DATA.digits[1] = data; + if (digit & DIGIT_3) + SEGMENT_DATA.digits[2] = data; + if (digit & DIGIT_4) + SEGMENT_DATA.digits[3] = data; + + return NO_ERROR; +} +t_error segment_set_mask(segment_mask mask) { + SEGMENT_DATA.mask = mask; + return NO_ERROR; +} + +t_error segment_refresh_display(void) { + if (!SEGMENT_DATA.enable) + return NO_ERROR; + t_error ret = NO_ERROR; + for (uint8_t idx = 0; idx < 4; idx++) { + if (!(SEGMENT_DATA.mask & BV((idx + 4)))) + continue; + // we set the digit selector to none such that when we update the digits pin -> we dont + // update digits we dont want + ret |= pca9555_write_masked(PCA9555_OUTPUT_PORT0, 0b11110000, 0b11110000); + ret |= pca9555_write(PCA9555_OUTPUT_PORT1, SEGMENT_DATA.digits[idx]); + ret |= pca9555_write_masked(PCA9555_OUTPUT_PORT0, ~BV((idx + 4)), 0b11110000); + } + return ret; +} + +void segment_enable(void) { + SEGMENT_DATA.enable = true; +} +void segment_disable(void) { + SEGMENT_DATA.enable = false; +} diff --git a/ex06/src/lib/spi.c b/ex06/src/lib/spi.c new file mode 100644 index 0000000..971a84f --- /dev/null +++ b/ex06/src/lib/spi.c @@ -0,0 +1,31 @@ +#include + +#include "lib/mystd.h" +#include "lib/spi.h" + +void spi_init(void) { + // set the correct pins as output + SPI_DDR |= BV(CS) | BV(MOSI) | BV(SCK); + + // enable SPI, set as master, and clock to fosc/128 + SPCR = BV(SPE) | BV(MSTR) | BV(SPR1) | BV(SPR0); +} + +void spi_uninit(void) { + SPCR = 0; +} + +t_error spi_write(uint8_t data) { + uint16_t o = 0; + + // load data into register + SPDR = data; + + // Wait for transmission complete + while (!(SPSR & BV(SPIF)) && o < 32768) + o++; + if (o >= 32768) + return ERROR; + + return NO_ERROR; +} diff --git a/ex06/src/lib/uart.c b/ex06/src/lib/uart.c new file mode 100644 index 0000000..8f6004f --- /dev/null +++ b/ex06/src/lib/uart.c @@ -0,0 +1,139 @@ +#include "lib/uart.h" +#include +#include "lib/mystd.h" +#include "lib/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) { + if (val == 0) + return uart_tx('0'); + 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) { + if (val == 0) + return uart_tx('0'); + 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_i16(int16_t val) { + if (val == 0) + return uart_tx('0'); + if (val == -32768) + return uart_sendstring("-32768"); + if (val < 0) { + uart_tx('-'); + val = -val; + } + uart_send_u16(val); +} + +void uart_send_u32(uint32_t val) { + if (val == 0) + return uart_tx('0'); + char buf[11] = {}; + uint8_t idx = 0; + bool print = false; + + uint32_t modulus = 1000000000; + 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/ex06/src/lib/utils.c b/ex06/src/lib/utils.c new file mode 100644 index 0000000..eb1a2a7 --- /dev/null +++ b/ex06/src/lib/utils.c @@ -0,0 +1,125 @@ +#include "lib/utils.h" +#include "lib/mystd.h" +#include "lib/uart.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; +} + +static float ft_pow(float base, float count) { + float out = 1; + + while (count--) + out *= base; + return out; +} + +// Reverses a string 'str' of length 'len' +static void reverse(char* str, uint16_t len) { + uint16_t i = 0, j = len - 1, temp; + while (i < j) { + temp = str[i]; + str[i] = str[j]; + str[j] = temp; + i++; + j--; + } +} + +static uint16_t ft_itoa(uint32_t value, char* out, uint16_t width) { + uint16_t i = 0; + if (value == 0) { + while (i < width) + out[i++] = '0'; + return i; + } + while (value) { + out[i++] = (value % 10) + '0'; + value = value / 10; + } + + // If number of digits required is more, then + // add 0s at the beginning + while (i < width) + out[i++] = '0'; + + // we need to reverse the data + reverse(out, i); + out[i] = 0; + + return i; +} +// 0.3250351 +// Converts a floating-point/double number to a string. +uint8_t ft_ftoa(float val, char* out, uint16_t precision) { + uint32_t ipart = (uint32_t)val; + float fpart = val - (float)ipart; + + // convert integer part to string + uint8_t i = ft_itoa(ipart, out, 1); + + if (precision != 0) { + out[i] = '.'; + fpart = fpart * ft_pow(10.f, precision); + i += ft_itoa((uint32_t)fpart, &out[i + 1], precision); + } + return i; +} + +void* ft_memcpy(void* dest, void* src, uint16_t len) { + for (uint16_t i = 0; i < len; i++) + ((uint8_t*)dest)[i] = ((uint8_t*)src)[i]; + return dest; +} + +bool ft_memcmp(void* s1, void* s2, uint16_t len) { + for (uint16_t i = 0; i < len; i++) { + if (((uint8_t*)s1)[i] != ((uint8_t*)s2)[i]) + return false; + } + return true; +} + +uint8_t is_hex_digit(char chr) { + uint8_t i; + if ((i = ft_stridx("0123456789", chr)) != 255) + return i; + if ((i = ft_stridx("abcdef", chr)) != 255) + return 10 + i; + if ((i = ft_stridx("ABCDEF", chr)) != 255) + return 10 + i; + return 255; +} diff --git a/ex06/src/main.c b/ex06/src/main.c new file mode 100644 index 0000000..2a741ec --- /dev/null +++ b/ex06/src/main.c @@ -0,0 +1,51 @@ +#include +#include + +#include "lib/adc.h" +#include "lib/aht20.h" +#include "lib/apa102.h" +#include "lib/eeprom.h" +#include "lib/i2c.h" +#include "lib/milis.h" +#include "lib/mystd.h" +#include "lib/pca9555.h" +#include "lib/pcf8563.h" +#include "lib/rgb.h" +#include "lib/segment.h" +#include "lib/spi.h" +#include "lib/uart.h" +#include "lib/utils.h" + +#define tabsize(tab) (sizeof(tab) / sizeof(tab[0])) + +segment_digit seg_digits[10] = { + SEG_0, SEG_1, SEG_2, SEG_3, SEG_4, SEG_5, SEG_6, SEG_7, SEG_8, SEG_9, +}; + +int main(void) { + milis_init(); + uart_init(); + i2c_init(); + pca9555_detect_addr(); + segment_init(); + adc_init(ADC_AVCC, ADC_PRESCALER_64); + + segment_set_mask(DIGIT_ALL); + segment_set_digit(SEG_0, DIGIT_ALL); + segment_enable(); + + timer_t adc_timer = timer_reset(); + + while (1) { + if (timer_wait(&adc_timer, 10)) { + uint16_t val = adc_read_pin_10bit(ADC_ADC0); + + adc_timer = timer_reset(); + segment_set_digit(seg_digits[(val / 1000) % 10], DIGIT_1); + segment_set_digit(seg_digits[(val / 100) % 10], DIGIT_2); + segment_set_digit(seg_digits[(val / 10) % 10], DIGIT_3); + segment_set_digit(seg_digits[(val / 1) % 10], DIGIT_4); + } + segment_refresh_display(); + } +}