From 91faba680045d90e0f710da50f1675c7aaf96dbd Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Mon, 23 Mar 2026 09:33:04 -0400 Subject: [PATCH] Updated Drivers --- drivers/0x01_uart/0x01_uart.c | 27 ++--- drivers/0x01_uart/uart.c | 13 +++ drivers/0x01_uart/uart.h | 11 ++ drivers/0x02_blink/0x02_blink.c | 13 ++- drivers/0x02_blink/blink.c | 5 + drivers/0x03_button/0x03_button.c | 43 +++++--- drivers/0x03_button/button.c | 13 ++- drivers/0x04_pwm/0x04_pwm.c | 42 +++++--- drivers/0x04_pwm/pwm.c | 31 ++++-- drivers/0x05_servo/0x05_servo.c | 47 ++++++--- drivers/0x05_servo/servo.c | 44 ++++---- drivers/0x06_adc/0x06_adc.c | 34 ++++-- drivers/0x06_adc/adc.c | 13 ++- drivers/0x07_i2c/0x07_i2c.c | 12 ++- drivers/0x07_i2c/i2c.c | 62 +++++++---- drivers/0x08_lcd1602/0x08_lcd1602.c | 55 ++++++---- drivers/0x08_lcd1602/lcd1602.c | 131 ++++++++++++++--------- drivers/0x09_dht11/0x09_dht11.c | 45 +++++--- drivers/0x09_dht11/dht11.c | 132 ++++++++++++++++++------ drivers/0x0a_ir/0x0a_ir.c | 29 ++++-- drivers/0x0a_ir/ir.c | 110 +++++++++++++++----- drivers/0x0b_spi/0x0b_spi.c | 46 ++++++--- drivers/0x0b_spi/spi.c | 32 ++++-- drivers/0x0c_multicore/0x0c_multicore.c | 46 +++++++-- drivers/0x0c_multicore/multicore.c | 2 + drivers/0x0d_timer/0x0d_timer.c | 23 +++-- drivers/0x0d_timer/timer.c | 23 +++-- drivers/0x0e_watchdog/0x0e_watchdog.c | 45 +++++--- drivers/0x0e_watchdog/watchdog.c | 2 + drivers/0x0f_flash/0x0f_flash.c | 46 ++++++--- drivers/0x0f_flash/flash.c | 1 + 31 files changed, 834 insertions(+), 344 deletions(-) diff --git a/drivers/0x01_uart/0x01_uart.c b/drivers/0x01_uart/0x01_uart.c index c375604..20c5fd7 100644 --- a/drivers/0x01_uart/0x01_uart.c +++ b/drivers/0x01_uart/0x01_uart.c @@ -46,32 +46,23 @@ #define UART_RX_PIN 1 #define UART_BAUD 115200 -/** - * @brief Convert a lowercase ASCII character to uppercase - * - * Returns the uppercase equivalent if the character is in 'a'-'z'; - * all other characters are passed through unchanged. - * - * @param c Input character - * @return char Uppercase equivalent, or the original character - */ -static char to_upper(char c) { - if (c >= 'a' && c <= 'z') { - return (char)(c - 32); - } - return c; -} +/** + * @brief Application entry point for the UART uppercase echo demo + * + * Initializes UART0 and enters an infinite loop that reads incoming + * characters, converts them to uppercase, and echoes them back. + * + * @return int Does not return + */ int main(void) { uart_driver_init(UART_TX_PIN, UART_RX_PIN, UART_BAUD); - uart_driver_puts("UART driver ready (115200 8N1)\r\n"); uart_driver_puts("Type characters to echo them back in UPPERCASE:\r\n"); - while (true) { if (uart_driver_is_readable()) { char c = uart_driver_getchar(); - char upper = to_upper(c); + char upper = uart_driver_to_upper(c); uart_driver_putchar(upper); } } diff --git a/drivers/0x01_uart/uart.c b/drivers/0x01_uart/uart.c index 0dfbf51..68d6418 100644 --- a/drivers/0x01_uart/uart.c +++ b/drivers/0x01_uart/uart.c @@ -34,26 +34,39 @@ #define UART_INST uart0 + void uart_driver_init(uint32_t tx_pin, uint32_t rx_pin, uint32_t baud_rate) { uart_init(UART_INST, baud_rate); gpio_set_function(tx_pin, GPIO_FUNC_UART); gpio_set_function(rx_pin, GPIO_FUNC_UART); } + bool uart_driver_is_readable(void) { return uart_is_readable(UART_INST); } + char uart_driver_getchar(void) { return (char)uart_getc(UART_INST); } + void uart_driver_putchar(char c) { uart_putc_raw(UART_INST, c); } + void uart_driver_puts(const char *str) { while (*str) { uart_putc_raw(UART_INST, *str++); } } + + +char uart_driver_to_upper(char c) { + if (c >= 'a' && c <= 'z') { + return (char)(c - 32); + } + return c; +} diff --git a/drivers/0x01_uart/uart.h b/drivers/0x01_uart/uart.h index f366a05..2de82c5 100644 --- a/drivers/0x01_uart/uart.h +++ b/drivers/0x01_uart/uart.h @@ -86,4 +86,15 @@ void uart_driver_putchar(char c); */ void uart_driver_puts(const char *str); +/** + * @brief Convert a lowercase ASCII character to uppercase + * + * Returns the uppercase equivalent if the character is in 'a'-'z'; + * all other characters are passed through unchanged. + * + * @param c Input character + * @return char Uppercase equivalent, or the original character + */ +char uart_driver_to_upper(char c); + #endif // UART_H diff --git a/drivers/0x02_blink/0x02_blink.c b/drivers/0x02_blink/0x02_blink.c index 16fb1ae..c74c1fc 100644 --- a/drivers/0x02_blink/0x02_blink.c +++ b/drivers/0x02_blink/0x02_blink.c @@ -43,15 +43,24 @@ #define LED_PIN 25 #define BLINK_DELAY_MS 500 + +/** + * @brief Application entry point for the LED blink demo + * + * Initializes the onboard LED and enters an infinite loop that + * toggles the LED state every BLINK_DELAY_MS milliseconds. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); blink_init(LED_PIN); - printf("Blink driver initialized on GPIO %d\r\n", LED_PIN); - while (true) { blink_toggle(LED_PIN); printf("LED: %s\r\n", blink_get_state(LED_PIN) ? "ON" : "OFF"); sleep_ms(BLINK_DELAY_MS); } +} + } } diff --git a/drivers/0x02_blink/blink.c b/drivers/0x02_blink/blink.c index d3f0af4..3c0e95a 100644 --- a/drivers/0x02_blink/blink.c +++ b/drivers/0x02_blink/blink.c @@ -31,24 +31,29 @@ #include "pico/stdlib.h" #include "hardware/gpio.h" + void blink_init(uint32_t pin) { gpio_init(pin); gpio_set_dir(pin, GPIO_OUT); gpio_put(pin, false); } + void blink_on(uint32_t pin) { gpio_put(pin, true); } + void blink_off(uint32_t pin) { gpio_put(pin, false); } + void blink_toggle(uint32_t pin) { gpio_put(pin, !gpio_get(pin)); } + bool blink_get_state(uint32_t pin) { return (bool)gpio_get(pin); } diff --git a/drivers/0x03_button/0x03_button.c b/drivers/0x03_button/0x03_button.c index aa1b558..8f3f77c 100644 --- a/drivers/0x03_button/0x03_button.c +++ b/drivers/0x03_button/0x03_button.c @@ -46,26 +46,41 @@ #define LED_PIN 25 #define DEBOUNCE_MS 20 + +/** + * @brief Poll button state and report edge transitions over UART + * + * Reads the debounced button state, mirrors it to the LED, and prints + * a message when the state changes from the previous reading. + * + * @param last_state Pointer to the stored previous button state + */ +static void _poll_button(bool *last_state) { + bool pressed = button_is_pressed(BUTTON_PIN); + button_led_set(LED_PIN, pressed); + if (pressed != *last_state) { + printf("Button: %s\r\n", pressed ? "PRESSED" : "RELEASED"); + *last_state = pressed; + } +} + + +/** + * @brief Application entry point for the button debounce demo + * + * Initializes button and LED, then continuously polls button state + * and mirrors it to the LED with UART reporting on edge transitions. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); button_init(BUTTON_PIN, DEBOUNCE_MS); button_led_init(LED_PIN); - - printf("Button driver initialized: button=GPIO%d led=GPIO%d\r\n", - BUTTON_PIN, LED_PIN); - + printf("Button driver initialized: button=GPIO%d led=GPIO%d\r\n", BUTTON_PIN, LED_PIN); bool last_state = false; - while (true) { - bool pressed = button_is_pressed(BUTTON_PIN); - - button_led_set(LED_PIN, pressed); - - if (pressed != last_state) { - printf("Button: %s\r\n", pressed ? "PRESSED" : "RELEASED"); - last_state = pressed; - } - + _poll_button(&last_state); sleep_ms(10); } } diff --git a/drivers/0x03_button/button.c b/drivers/0x03_button/button.c index e0bb2df..f21b87a 100644 --- a/drivers/0x03_button/button.c +++ b/drivers/0x03_button/button.c @@ -33,6 +33,7 @@ static uint32_t debounce_delay_ms = 20; + /** * @brief Confirm a raw active-low pin read by waiting for the debounce period * @@ -43,11 +44,12 @@ static uint32_t debounce_delay_ms = 20; * @param pin GPIO pin number to re-sample * @return bool true if the pin is still low after the debounce delay */ -static bool debounce_confirm(uint32_t pin) { +static bool _debounce_confirm(uint32_t pin) { sleep_ms(debounce_delay_ms); return !gpio_get(pin); } + void button_init(uint32_t pin, uint32_t debounce_ms) { debounce_delay_ms = debounce_ms; gpio_init(pin); @@ -55,19 +57,26 @@ void button_init(uint32_t pin, uint32_t debounce_ms) { gpio_pull_up(pin); } + bool button_is_pressed(uint32_t pin) { if (!gpio_get(pin)) { - return debounce_confirm(pin); + return _debounce_confirm(pin); } return false; } + void button_led_init(uint32_t pin) { gpio_init(pin); gpio_set_dir(pin, GPIO_OUT); gpio_put(pin, false); } + +void button_led_set(uint32_t pin, bool on) { + gpio_put(pin, on); +} + void button_led_set(uint32_t pin, bool on) { gpio_put(pin, on); } diff --git a/drivers/0x04_pwm/0x04_pwm.c b/drivers/0x04_pwm/0x04_pwm.c index 1688519..8c4da9e 100644 --- a/drivers/0x04_pwm/0x04_pwm.c +++ b/drivers/0x04_pwm/0x04_pwm.c @@ -44,22 +44,40 @@ #define PWM_PIN 0 #define PWM_FREQ_HZ 1000 + +/** + * @brief Sweep the PWM duty cycle between start and end in given steps + * + * Iterates from start to end with the given step increment, updating + * the PWM duty cycle and printing each value with a 50 ms delay. + * + * @param start Starting duty percentage + * @param end Ending duty percentage + * @param step Increment per iteration (negative for descending) + */ +static void _sweep_duty(int start, int end, int step) { + for (int duty = start; (step > 0) ? duty <= end : duty >= end; duty += step) { + pwm_driver_set_duty_percent((uint8_t)duty); + printf("Duty: %3d%%\r\n", duty); + sleep_ms(50); + } +} + + +/** + * @brief Application entry point for the PWM LED breathing demo + * + * Initializes PWM at 1 kHz and sweeps the duty cycle up and down + * to produce a smooth LED breathing effect, reporting over UART. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); pwm_driver_init(PWM_PIN, PWM_FREQ_HZ); - printf("PWM driver initialized: GPIO%d @ %d Hz\r\n", PWM_PIN, PWM_FREQ_HZ); - while (true) { - for (int duty = 0; duty <= 100; duty += 5) { - pwm_driver_set_duty_percent((uint8_t)duty); - printf("Duty: %3d%%\r\n", duty); - sleep_ms(50); - } - for (int duty = 100; duty >= 0; duty -= 5) { - pwm_driver_set_duty_percent((uint8_t)duty); - printf("Duty: %3d%%\r\n", duty); - sleep_ms(50); - } + _sweep_duty(0, 100, 5); + _sweep_duty(100, 0, -5); } } diff --git a/drivers/0x04_pwm/pwm.c b/drivers/0x04_pwm/pwm.c index 541132a..6f2f042 100644 --- a/drivers/0x04_pwm/pwm.c +++ b/drivers/0x04_pwm/pwm.c @@ -36,6 +36,7 @@ static uint pwm_slice; static uint pwm_chan; static uint32_t pwm_wrap; + /** * @brief Compute the PWM clock divider that yields the target frequency * @@ -47,24 +48,38 @@ static uint32_t pwm_wrap; * @param wrap_val Chosen PWM counter wrap value (period - 1) * @return float Clock divider to program into the PWM slice */ -static float calc_clk_div(uint32_t freq_hz, uint32_t wrap_val) { +static float _calc_clk_div(uint32_t freq_hz, uint32_t wrap_val) { uint32_t sys_hz = clock_get_hz(clk_sys); return (float)sys_hz / ((float)freq_hz * (float)(wrap_val + 1)); } + +/** + * @brief Apply the PWM configuration to the active slice + * + * Builds a default config, sets the clock divider for the target frequency, + * programs the wrap value, starts the slice, and zeroes the channel level. + * + * @param freq_hz Desired PWM output frequency in Hz + */ +static void _apply_pwm_config(uint32_t freq_hz) { + pwm_config cfg = pwm_get_default_config(); + pwm_config_set_clkdiv(&cfg, _calc_clk_div(freq_hz, pwm_wrap)); + pwm_config_set_wrap(&cfg, pwm_wrap); + pwm_init(pwm_slice, &cfg, true); + pwm_set_chan_level(pwm_slice, pwm_chan, 0); +} + + void pwm_driver_init(uint32_t pin, uint32_t freq_hz) { gpio_set_function(pin, GPIO_FUNC_PWM); pwm_slice = pwm_gpio_to_slice_num(pin); pwm_chan = pwm_gpio_to_channel(pin); - pwm_wrap = 10000 - 1; // resolution: 0.01% steps - - pwm_config cfg = pwm_get_default_config(); - pwm_config_set_clkdiv(&cfg, calc_clk_div(freq_hz, pwm_wrap)); - pwm_config_set_wrap(&cfg, pwm_wrap); - pwm_init(pwm_slice, &cfg, true); - pwm_set_chan_level(pwm_slice, pwm_chan, 0); + pwm_wrap = 10000 - 1; + _apply_pwm_config(freq_hz); } + void pwm_driver_set_duty_percent(uint8_t percent) { if (percent > 100) { percent = 100; diff --git a/drivers/0x05_servo/0x05_servo.c b/drivers/0x05_servo/0x05_servo.c index 3e7a41a..004b426 100644 --- a/drivers/0x05_servo/0x05_servo.c +++ b/drivers/0x05_servo/0x05_servo.c @@ -47,24 +47,43 @@ #define STEP_DEGREES 10 #define STEP_DELAY_MS 150 + +/** + * @brief Sweep the servo between start and end angles in given steps + * + * Iterates from start to end with the given step increment, setting + * each angle and printing the current position with a delay between steps. + * + * @param start Starting angle in degrees + * @param end Ending angle in degrees + * @param step Increment per iteration (negative for descending) + */ +static void _sweep_angle(int start, int end, int step) { + for (int angle = start; (step > 0) ? angle <= end : angle >= end; angle += step) { + servo_set_angle((float)angle); + printf("Angle: %3d deg\r\n", angle); + sleep_ms(STEP_DELAY_MS); + } +} + + +/** + * @brief Application entry point for the servo sweep demo + * + * Initializes the servo on GPIO and continuously sweeps 0-180-0 degrees + * in STEP_DEGREES increments, reporting each angle over UART. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); servo_init(SERVO_GPIO); - printf("Servo driver initialized on GPIO %d\r\n", SERVO_GPIO); - printf("Sweeping 0 -> 180 -> 0 degrees in %d-degree steps\r\n", - STEP_DEGREES); - + printf("Sweeping 0 -> 180 -> 0 degrees in %d-degree steps\r\n", STEP_DEGREES); while (true) { - for (int angle = 0; angle <= 180; angle += STEP_DEGREES) { - servo_set_angle((float)angle); - printf("Angle: %3d deg\r\n", angle); - sleep_ms(STEP_DELAY_MS); - } - for (int angle = 180; angle >= 0; angle -= STEP_DEGREES) { - servo_set_angle((float)angle); - printf("Angle: %3d deg\r\n", angle); - sleep_ms(STEP_DELAY_MS); - } + _sweep_angle(0, 180, STEP_DEGREES); + _sweep_angle(180, 0, -STEP_DEGREES); + } +} } } diff --git a/drivers/0x05_servo/servo.c b/drivers/0x05_servo/servo.c index 4cf3481..07bbb96 100644 --- a/drivers/0x05_servo/servo.c +++ b/drivers/0x05_servo/servo.c @@ -32,18 +32,17 @@ #include "hardware/pwm.h" #include "hardware/clocks.h" -// Default servo pulse range (SG90 typical) static const uint16_t SERVO_DEFAULT_MIN_US = 1000; static const uint16_t SERVO_DEFAULT_MAX_US = 2000; -// internal state static uint8_t servo_pin = 0; static uint servo_slice = 0; static uint servo_chan = 0; -static uint32_t servo_wrap = 20000 - 1; // wrap to map microseconds for 50Hz +static uint32_t servo_wrap = 20000 - 1; static float servo_hz = 50.0f; static bool servo_initialized = false; + /** * @brief Convert a pulse width in microseconds to a PWM counter level * @@ -53,45 +52,52 @@ static bool servo_initialized = false; * @param pulse_us Pulse width in microseconds * @return uint32_t PWM level suitable for pwm_set_chan_level() */ -static uint32_t pulse_us_to_level(uint32_t pulse_us) { - const float period_us = 1000000.0f / servo_hz; // 20000us +static uint32_t _pulse_us_to_level(uint32_t pulse_us) { + const float period_us = 1000000.0f / servo_hz; float counts_per_us = (servo_wrap + 1) / period_us; return (uint32_t)(pulse_us * counts_per_us + 0.5f); } -void servo_init(uint8_t pin) { - servo_pin = pin; - - // Configure GPIO for PWM - gpio_set_function(servo_pin, GPIO_FUNC_PWM); - servo_slice = pwm_gpio_to_slice_num(servo_pin); - servo_chan = pwm_gpio_to_channel(servo_pin); +/** + * @brief Build and apply the PWM slice configuration for 50 Hz servo + * + * Computes the clock divider from the system clock to achieve the + * target servo frequency with the chosen wrap value, then starts + * the PWM slice. + */ +static void _apply_servo_config(void) { pwm_config config = pwm_get_default_config(); - - // Calculate clock divider to achieve 50 Hz with our chosen wrap const uint32_t sys_clock_hz = clock_get_hz(clk_sys); float clock_div = (float)sys_clock_hz / (servo_hz * (servo_wrap + 1)); - pwm_config_set_clkdiv(&config, clock_div); pwm_config_set_wrap(&config, servo_wrap); pwm_init(servo_slice, &config, true); +} + + +void servo_init(uint8_t pin) { + servo_pin = pin; + gpio_set_function(servo_pin, GPIO_FUNC_PWM); + servo_slice = pwm_gpio_to_slice_num(servo_pin); + servo_chan = pwm_gpio_to_channel(servo_pin); + _apply_servo_config(); servo_initialized = true; } + void servo_set_pulse_us(uint16_t pulse_us) { - if (!servo_initialized) return; // not initialized - // clamp to defaults + if (!servo_initialized) return; if (pulse_us < SERVO_DEFAULT_MIN_US) pulse_us = SERVO_DEFAULT_MIN_US; if (pulse_us > SERVO_DEFAULT_MAX_US) pulse_us = SERVO_DEFAULT_MAX_US; - uint32_t level = pulse_us_to_level(pulse_us); + uint32_t level = _pulse_us_to_level(pulse_us); pwm_set_chan_level(servo_slice, servo_chan, level); } + void servo_set_angle(float degrees) { if (degrees < 0.0f) degrees = 0.0f; if (degrees > 180.0f) degrees = 180.0f; - // linear map 0..180 -> min_us..max_us float ratio = degrees / 180.0f; uint16_t pulse = (uint16_t)(SERVO_DEFAULT_MIN_US + ratio * (SERVO_DEFAULT_MAX_US - SERVO_DEFAULT_MIN_US) + 0.5f); servo_set_pulse_us(pulse); diff --git a/drivers/0x06_adc/0x06_adc.c b/drivers/0x06_adc/0x06_adc.c index 750db77..beed5f2 100644 --- a/drivers/0x06_adc/0x06_adc.c +++ b/drivers/0x06_adc/0x06_adc.c @@ -45,20 +45,34 @@ #define ADC_GPIO 26 #define ADC_CHANNEL 0 + +/** + * @brief Read and print ADC voltage and chip temperature over UART + * + * Samples the ADC channel for voltage in millivolts and reads the + * on-chip temperature sensor, then prints both values. + */ +static void _print_adc_readings(void) { + uint32_t voltage_mv = adc_driver_read_mv(); + float temp_c = adc_driver_read_temp_celsius(); + printf("ADC0: %4lu mV | Chip temp: %.1f C\r\n", voltage_mv, temp_c); +} + + +/** + * @brief Application entry point for the ADC voltage and temperature demo + * + * Initializes the ADC on GPIO26 channel 0 and prints readings + * every 500 ms over UART. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); adc_driver_init(ADC_GPIO, ADC_CHANNEL); - - printf("ADC driver initialized: GPIO%d (channel %d)\r\n", - ADC_GPIO, ADC_CHANNEL); - + printf("ADC driver initialized: GPIO%d (channel %d)\r\n", ADC_GPIO, ADC_CHANNEL); while (true) { - uint32_t voltage_mv = adc_driver_read_mv(); - float temp_c = adc_driver_read_temp_celsius(); - - printf("ADC0: %4lu mV | Chip temp: %.1f C\r\n", - voltage_mv, temp_c); - + _print_adc_readings(); sleep_ms(500); } } diff --git a/drivers/0x06_adc/adc.c b/drivers/0x06_adc/adc.c index 5e19e06..d64ec57 100644 --- a/drivers/0x06_adc/adc.c +++ b/drivers/0x06_adc/adc.c @@ -36,6 +36,7 @@ static uint8_t active_channel = 0; + /** * @brief Convert a raw 12-bit ADC value to millivolts * @@ -44,10 +45,11 @@ static uint8_t active_channel = 0; * @param raw 12-bit ADC conversion result (0 - 4095) * @return uint32_t Equivalent voltage in millivolts (0 - 3300) */ -static uint32_t raw_to_mv(uint16_t raw) { +static uint32_t _raw_to_mv(uint16_t raw) { return (uint32_t)raw * ADC_VREF_MV / ADC_FULL_SCALE; } + /** * @brief Convert a raw temperature-sensor ADC value to degrees Celsius * @@ -57,11 +59,12 @@ static uint32_t raw_to_mv(uint16_t raw) { * @param raw 12-bit ADC result from the internal temperature sensor (channel 4) * @return float Die temperature in degrees Celsius */ -static float raw_to_celsius(uint16_t raw) { +static float _raw_to_celsius(uint16_t raw) { float voltage = (float)raw * 3.3f / (float)ADC_FULL_SCALE; return 27.0f - (voltage - 0.706f) / 0.001721f; } + void adc_driver_init(uint32_t gpio, uint8_t channel) { active_channel = channel; adc_init(); @@ -70,13 +73,15 @@ void adc_driver_init(uint32_t gpio, uint8_t channel) { adc_select_input(channel); } + uint32_t adc_driver_read_mv(void) { - return raw_to_mv(adc_read()); + return _raw_to_mv(adc_read()); } + float adc_driver_read_temp_celsius(void) { adc_select_input(4); - float result = raw_to_celsius(adc_read()); + float result = _raw_to_celsius(adc_read()); adc_select_input(active_channel); return result; } diff --git a/drivers/0x07_i2c/0x07_i2c.c b/drivers/0x07_i2c/0x07_i2c.c index cf47b80..0bda32c 100644 --- a/drivers/0x07_i2c/0x07_i2c.c +++ b/drivers/0x07_i2c/0x07_i2c.c @@ -49,13 +49,21 @@ #define I2C_SCL_PIN 3 #define I2C_BAUD 100000 + +/** + * @brief Application entry point for the I2C bus scanner demo + * + * Initializes I2C1 at 100 kHz on SDA=GPIO2 / SCL=GPIO3 and prints + * a formatted hex table of all responding device addresses over UART, + * repeating every 5 seconds. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); i2c_driver_init(I2C_PORT, I2C_SDA_PIN, I2C_SCL_PIN, I2C_BAUD); - printf("I2C driver initialized: I2C%d @ %d Hz SDA=GPIO%d SCL=GPIO%d\r\n", I2C_PORT, I2C_BAUD, I2C_SDA_PIN, I2C_SCL_PIN); - while (true) { i2c_driver_scan(I2C_PORT); sleep_ms(5000); diff --git a/drivers/0x07_i2c/i2c.c b/drivers/0x07_i2c/i2c.c index 1a09d4f..b93ed2c 100644 --- a/drivers/0x07_i2c/i2c.c +++ b/drivers/0x07_i2c/i2c.c @@ -33,13 +33,20 @@ #include "hardware/i2c.h" #include "hardware/gpio.h" -static i2c_inst_t *get_i2c_inst(uint8_t port) { +/** + * @brief Map an I2C port number to its hardware instance pointer + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @return i2c_inst_t* Pointer to the corresponding I2C hardware instance + */ +static i2c_inst_t *_get_i2c_inst(uint8_t port) { return port == 0 ? i2c0 : i2c1; } + void i2c_driver_init(uint8_t port, uint32_t sda_pin, uint32_t scl_pin, uint32_t baud_hz) { - i2c_inst_t *inst = get_i2c_inst(port); + i2c_inst_t *inst = _get_i2c_inst(port); i2c_init(inst, baud_hz); gpio_set_function(sda_pin, GPIO_FUNC_I2C); gpio_set_function(scl_pin, GPIO_FUNC_I2C); @@ -47,29 +54,44 @@ void i2c_driver_init(uint8_t port, uint32_t sda_pin, uint32_t scl_pin, gpio_pull_up(scl_pin); } + bool i2c_driver_probe(uint8_t port, uint8_t addr) { - i2c_inst_t *inst = get_i2c_inst(port); + i2c_inst_t *inst = _get_i2c_inst(port); uint8_t dummy; return i2c_read_blocking(inst, addr, &dummy, 1, false) >= 0; } -void i2c_driver_scan(uint8_t port) { + +/** + * @brief Print the I2C scan table header over UART + */ +static void _print_scan_header(void) { printf("\r\nI2C bus scan:\r\n"); printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); - - for (uint8_t addr = 0; addr < 128; addr++) { - if (addr % 16 == 0) { - printf("%02X: ", addr); - } - if (addr < 0x08 || addr > 0x77) { - printf(" "); - } else if (i2c_driver_probe(port, addr)) { - printf("%02X ", addr); - } else { - printf("-- "); - } - if (addr % 16 == 15) { - printf("\r\n"); - } - } +} + + +/** + * @brief Print one cell of the scan table for a given address + * + * Prints the row label when the address is at a 16-byte boundary, then + * prints the address if a device responds, dashes if not, or blank if + * the address is in the reserved range. Ends the row at every 16th address. + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @param addr 7-bit I2C address being probed + */ +static void _print_scan_entry(uint8_t port, uint8_t addr) { + if (addr % 16 == 0) printf("%02X: ", addr); + if (addr < 0x08 || addr > 0x77) printf(" "); + else if (i2c_driver_probe(port, addr)) printf("%02X ", addr); + else printf("-- "); + if (addr % 16 == 15) printf("\r\n"); +} + + +void i2c_driver_scan(uint8_t port) { + _print_scan_header(); + for (uint8_t addr = 0; addr < 128; addr++) + _print_scan_entry(port, addr); } diff --git a/drivers/0x08_lcd1602/0x08_lcd1602.c b/drivers/0x08_lcd1602/0x08_lcd1602.c index 6a1ca59..335b60c 100644 --- a/drivers/0x08_lcd1602/0x08_lcd1602.c +++ b/drivers/0x08_lcd1602/0x08_lcd1602.c @@ -51,26 +51,45 @@ #define I2C_BAUD_HZ 100000 #define LCD_I2C_ADDR 0x27 -int main(void) { - stdio_init_all(); - - lcd_init(I2C_PORT, I2C_SDA_PIN, I2C_SCL_PIN, I2C_BAUD_HZ, - LCD_I2C_ADDR, 4, 0x08); +/** + * @brief Initialize the LCD, display the title, and log over UART + */ +static void _setup_display(void) { + lcd_init(I2C_PORT, I2C_SDA_PIN, I2C_SCL_PIN, I2C_BAUD_HZ, LCD_I2C_ADDR, 4, 0x08); lcd_set_cursor(0, 0); lcd_puts("Reverse Eng."); - printf("LCD 1602 driver initialized at I2C addr 0x%02X\r\n", LCD_I2C_ADDR); - - uint32_t count = 0; - char buf[17]; - - while (true) { - snprintf(buf, sizeof(buf), "Count: %6lu", count++); - lcd_set_cursor(1, 0); - lcd_puts(buf); - - printf("%s\r\n", buf); - sleep_ms(1000); - } +} + + +/** + * @brief Format and display the next counter value on LCD line 1 + * + * @param count Pointer to the running counter (post-incremented) + */ +static void _update_counter(uint32_t *count) { + char buf[17]; + snprintf(buf, sizeof(buf), "Count: %6lu", (*count)++); + lcd_set_cursor(1, 0); + lcd_puts(buf); + printf("%s\r\n", buf); + sleep_ms(1000); +} + + +/** + * @brief Application entry point for the LCD 1602 counter demo + * + * Initializes the LCD over I2C with a static title on line 0 and + * continuously increments a counter on line 1 every second. + * + * @return int Does not return + */ +int main(void) { + stdio_init_all(); + _setup_display(); + uint32_t count = 0; + while (true) + _update_counter(&count); } diff --git a/drivers/0x08_lcd1602/lcd1602.c b/drivers/0x08_lcd1602/lcd1602.c index 01bca34..513eda6 100644 --- a/drivers/0x08_lcd1602/lcd1602.c +++ b/drivers/0x08_lcd1602/lcd1602.c @@ -33,10 +33,17 @@ #include "pico/stdlib.h" #include "hardware/i2c.h" -static i2c_inst_t *get_i2c_inst(uint8_t port) { +/** + * @brief Map an I2C port number to its hardware instance pointer + * + * @param port I2C port number (0 for i2c0, 1 for i2c1) + * @return i2c_inst_t* Pointer to the corresponding I2C hardware instance + */ +static i2c_inst_t *_get_i2c_inst(uint8_t port) { return port == 0 ? i2c0 : i2c1; } + static i2c_inst_t *lcd_i2c = NULL; static uint8_t lcd_addr = 0x27; static int lcd_nibble_shift = 4; @@ -47,109 +54,139 @@ static uint8_t lcd_backlight_mask = 0x08; #define PIN_RW 0x02 #define PIN_EN 0x04 + /** * @brief Write one raw byte to the PCF8574 expander over I2C * * @param data Output byte to send to the expander */ -static void pcf_write_byte(uint8_t data) { +static void _pcf_write_byte(uint8_t data) { if (!lcd_i2c) return; i2c_write_blocking(lcd_i2c, lcd_addr, &data, 1, false); } + /** * @brief Toggle EN to latch a nibble into the LCD controller * * @param data Current control/data bus byte (with RS and backlight already set) */ -static void pcf_pulse_enable(uint8_t data) { - pcf_write_byte(data | PIN_EN); +static void _pcf_pulse_enable(uint8_t data) { + _pcf_write_byte(data | PIN_EN); sleep_us(1); - pcf_write_byte(data & ~PIN_EN); + _pcf_write_byte(data & ~PIN_EN); sleep_us(50); } + /** * @brief Write one 4-bit nibble to the LCD * * @param nibble Lower 4 bits to write * @param mode 0 for command, non-zero for character data */ -static void lcd_write4(uint8_t nibble, uint8_t mode) { +static void _lcd_write4(uint8_t nibble, uint8_t mode) { uint8_t data = (nibble & 0x0F) << lcd_nibble_shift; data |= mode ? PIN_RS : 0; data |= lcd_backlight_mask; - pcf_pulse_enable(data); + _pcf_pulse_enable(data); } + /** * @brief Send one full 8-bit command/data value as two nibbles * * @param value Byte to send to the LCD * @param mode 0 for command, non-zero for character data */ -static void lcd_send(uint8_t value, uint8_t mode) { - lcd_write4((value >> 4) & 0x0F, mode); - lcd_write4(value & 0x0F, mode); +static void _lcd_send(uint8_t value, uint8_t mode) { + _lcd_write4((value >> 4) & 0x0F, mode); + _lcd_write4(value & 0x0F, mode); } + +/** + * @brief Store LCD driver configuration in module-level state + * + * @param i2c_port I2C port number (0 or 1) + * @param pcf_addr 7-bit PCF8574 address + * @param nibble_shift Bit shift for 4-bit nibbles + * @param backlight_mask Backlight control bit mask + */ +static void _lcd_store_config(uint8_t i2c_port, uint8_t pcf_addr, + int nibble_shift, uint8_t backlight_mask) { + lcd_i2c = _get_i2c_inst(i2c_port); + lcd_addr = pcf_addr; + lcd_nibble_shift = nibble_shift; + lcd_backlight_mask = backlight_mask; +} + + +/** + * @brief Execute the HD44780 4-bit mode power-on reset sequence + */ +static void _lcd_hd44780_reset(void) { + _lcd_write4(0x03, 0); + sleep_ms(5); + _lcd_write4(0x03, 0); + sleep_us(150); + _lcd_write4(0x03, 0); + sleep_us(150); + _lcd_write4(0x02, 0); + sleep_us(150); +} + + +/** + * @brief Send post-reset configuration commands to the HD44780 + * + * Sets 4-bit mode with 2 display lines, turns the display on with + * cursor hidden, clears the screen, and selects left-to-right entry mode. + */ +static void _lcd_hd44780_configure(void) { + _lcd_send(0x28, 0); + _lcd_send(0x0C, 0); + _lcd_send(0x01, 0); + sleep_ms(2); + _lcd_send(0x06, 0); +} + + +void lcd_i2c_init(uint8_t i2c_port, uint8_t pcf_addr, int nibble_shift, + uint8_t backlight_mask) { + _lcd_store_config(i2c_port, pcf_addr, nibble_shift, backlight_mask); + _lcd_hd44780_reset(); + _lcd_hd44780_configure(); +} + + void lcd_init(uint8_t i2c_port, uint32_t sda_pin, uint32_t scl_pin, uint32_t baud_hz, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask) { - i2c_inst_t *i2c = get_i2c_inst(i2c_port); + i2c_inst_t *i2c = _get_i2c_inst(i2c_port); i2c_init(i2c, baud_hz); gpio_set_function(sda_pin, GPIO_FUNC_I2C); gpio_set_function(scl_pin, GPIO_FUNC_I2C); gpio_pull_up(sda_pin); gpio_pull_up(scl_pin); - lcd_i2c_init(i2c_port, pcf_addr, nibble_shift, backlight_mask); } -void lcd_i2c_init(uint8_t i2c_port, uint8_t pcf_addr, int nibble_shift, uint8_t backlight_mask) { - lcd_i2c = get_i2c_inst(i2c_port); - lcd_addr = pcf_addr; - lcd_nibble_shift = nibble_shift; - lcd_backlight_mask = backlight_mask; - - // Follow init sequence for HD44780 in 4-bit mode - lcd_write4(0x03, 0); - sleep_ms(5); - lcd_write4(0x03, 0); - sleep_us(150); - lcd_write4(0x03, 0); - sleep_us(150); - lcd_write4(0x02, 0); - sleep_us(150); - - // Function set: 4-bit, 2 lines - lcd_send(0x28, 0); - - // Display on, cursor off - lcd_send(0x0C, 0); - - // Clear - lcd_send(0x01, 0); - sleep_ms(2); - - // Entry mode set - lcd_send(0x06, 0); -} void lcd_clear(void) { - lcd_send(0x01, 0); + _lcd_send(0x01, 0); sleep_ms(2); } + void lcd_set_cursor(int line, int position) { const uint8_t row_offsets[] = {0x00, 0x40}; - if (line > 1) line = 1; - lcd_send(0x80 | (position + row_offsets[line]), 0); + _lcd_send(0x80 | (position + row_offsets[line]), 0); } + void lcd_puts(const char *s) { - while (*s) { - lcd_send((uint8_t)*s++, 1); - } + while (*s) + _lcd_send((uint8_t)*s++, 1); } diff --git a/drivers/0x09_dht11/0x09_dht11.c b/drivers/0x09_dht11/0x09_dht11.c index 12fbc24..f758a1a 100644 --- a/drivers/0x09_dht11/0x09_dht11.c +++ b/drivers/0x09_dht11/0x09_dht11.c @@ -45,24 +45,37 @@ #define DHT11_GPIO 4 + +/** + * @brief Read the DHT11 sensor and print the result over UART + * + * Attempts a single read. On success prints humidity and temperature; + * on failure prints a wiring-check message. Waits 2 s before returning + * to respect the DHT11 minimum polling interval. + */ +static void _print_reading(void) { + float humidity = 0.0f; + float temperature = 0.0f; + if (dht11_read(&humidity, &temperature)) + printf("Humidity: %.1f%% Temperature: %.1f C\r\n", humidity, temperature); + else + printf("DHT11 read failed - check wiring on GPIO %d\r\n", DHT11_GPIO); + sleep_ms(2000); +} + + +/** + * @brief Application entry point for the DHT11 sensor demo + * + * Initializes the DHT11 on GPIO4 and continuously reads temperature + * and humidity, printing results over UART every 2 seconds. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); dht11_init(DHT11_GPIO); - printf("DHT11 driver initialized on GPIO %d\r\n", DHT11_GPIO); - - while (true) { - float humidity = 0.0f; - float temperature = 0.0f; - - if (dht11_read(&humidity, &temperature)) { - printf("Humidity: %.1f%% Temperature: %.1f C\r\n", - humidity, temperature); - } else { - printf("DHT11 read failed - check wiring on GPIO %d\r\n", - DHT11_GPIO); - } - - sleep_ms(2000); - } + while (true) + _print_reading(); } diff --git a/drivers/0x09_dht11/dht11.c b/drivers/0x09_dht11/dht11.c index 9a52ba8..deebec8 100644 --- a/drivers/0x09_dht11/dht11.c +++ b/drivers/0x09_dht11/dht11.c @@ -33,46 +33,114 @@ static uint dht_pin; -void dht11_init(uint8_t pin) { - dht_pin = pin; - gpio_init(pin); - gpio_pull_up(pin); -} -bool dht11_read(float *humidity, float *temperature) { - uint8_t data[5] = {0}; - - // Start signal +/** + * @brief Send the DHT11 start signal on the data pin + * + * Drives the pin LOW for 18 ms then HIGH for 40 us before switching + * the pin to input mode to listen for the sensor response. + */ +static void _send_start_signal(void) { gpio_set_dir(dht_pin, GPIO_OUT); gpio_put(dht_pin, 0); sleep_ms(18); gpio_put(dht_pin, 1); sleep_us(40); gpio_set_dir(dht_pin, GPIO_IN); - - // Wait for response +} + + +/** + * @brief Wait for the pin to leave a given logic level + * + * Spins until the pin no longer reads the specified level, returning + * false if a timeout of 10 000 iterations is exceeded. + * + * @param level Logic level to wait through (0 or 1) + * @return bool true once the level changed, false on timeout + */ +static bool _wait_for_level(int level) { uint32_t timeout = 10000; - while (gpio_get(dht_pin) == 1) if (--timeout == 0) return false; - timeout = 10000; - while (gpio_get(dht_pin) == 0) if (--timeout == 0) return false; - timeout = 10000; - while (gpio_get(dht_pin) == 1) if (--timeout == 0) return false; - - // Read 40 bits - for (int i = 0; i < 40; i++) { - timeout = 10000; - while (gpio_get(dht_pin) == 0) if (--timeout == 0) return false; - uint32_t start = time_us_32(); - timeout = 10000; - while (gpio_get(dht_pin) == 1) if (--timeout == 0) return false; - uint32_t duration = time_us_32() - start; - data[i / 8] <<= 1; - if (duration > 40) data[i / 8] |= 1; - } - - // Check checksum - if (data[4] != ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) return false; - + while (gpio_get(dht_pin) == level) + if (--timeout == 0) return false; + return true; +} + + +/** + * @brief Wait for the DHT11 response after the start signal + * + * The sensor pulls LOW then HIGH then LOW again; each transition + * is awaited with a timeout. + * + * @return bool true if the full response was received, false on timeout + */ +static bool _wait_response(void) { + if (!_wait_for_level(1)) return false; + if (!_wait_for_level(0)) return false; + if (!_wait_for_level(1)) return false; + return true; +} + + +/** + * @brief Read a single bit from the DHT11 data stream + * + * Waits for the low-period to end, measures the high-period duration, + * and shifts the result into the appropriate byte of the data array. + * + * @param data 5-byte array accumulating the received bits + * @param i Bit index (0-39) + * @return bool true on success, false on timeout + */ +static bool _read_bit(uint8_t *data, int i) { + if (!_wait_for_level(0)) return false; + uint32_t start = time_us_32(); + if (!_wait_for_level(1)) return false; + uint32_t duration = time_us_32() - start; + data[i / 8] <<= 1; + if (duration > 40) data[i / 8] |= 1; + return true; +} + + +/** + * @brief Read all 40 data bits from the DHT11 + * + * @param data 5-byte array filled with the received data + * @return bool true if all 40 bits were read, false on timeout + */ +static bool _read_40_bits(uint8_t *data) { + for (int i = 0; i < 40; i++) + if (!_read_bit(data, i)) return false; + return true; +} + + +/** + * @brief Verify the DHT11 checksum byte + * + * @param data 5-byte received data (bytes 0-3 plus checksum in byte 4) + * @return bool true if the checksum matches, false otherwise + */ +static bool _validate_checksum(const uint8_t *data) { + return data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF); +} + + +void dht11_init(uint8_t pin) { + dht_pin = pin; + gpio_init(pin); + gpio_pull_up(pin); +} + + +bool dht11_read(float *humidity, float *temperature) { + uint8_t data[5] = {0}; + _send_start_signal(); + if (!_wait_response()) return false; + if (!_read_40_bits(data)) return false; + if (!_validate_checksum(data)) return false; *humidity = data[0] + data[1] * 0.1f; *temperature = data[2] + data[3] * 0.1f; return true; diff --git a/drivers/0x0a_ir/0x0a_ir.c b/drivers/0x0a_ir/0x0a_ir.c index 36aa2dc..763d92f 100644 --- a/drivers/0x0a_ir/0x0a_ir.c +++ b/drivers/0x0a_ir/0x0a_ir.c @@ -45,17 +45,30 @@ #define IR_GPIO 5 + +/** + * @brief Poll for an NEC frame and print the command if received + */ +static void _poll_and_print(void) { + int command = ir_getkey(); + if (command >= 0) + printf("NEC command: 0x%02X (%d)\r\n", command, command); +} + + +/** + * @brief Application entry point for the NEC IR receiver demo + * + * Initializes the IR receiver on GPIO5 and continuously decodes + * NEC frames, printing each command byte over UART. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); ir_init(IR_GPIO); - printf("NEC IR driver initialized on GPIO %d\r\n", IR_GPIO); printf("Press a button on your NEC remote...\r\n"); - - while (true) { - int command = ir_getkey(); - if (command >= 0) { - printf("NEC command: 0x%02X (%d)\r\n", command, command); - } - } + while (true) + _poll_and_print(); } diff --git a/drivers/0x0a_ir/ir.c b/drivers/0x0a_ir/ir.c index b89bd4e..e5ef44c 100644 --- a/drivers/0x0a_ir/ir.c +++ b/drivers/0x0a_ir/ir.c @@ -34,17 +34,92 @@ static unsigned int ir_pin = 0; -// Wait for 'gpio' to reach 'level' or timeout (microseconds). Return elapsed us or -1. -static int64_t wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { + +/** + * @brief Wait for a GPIO pin to reach a given logic level + * + * Spins until the pin matches the requested level or a microsecond timeout + * is exceeded. Returns the elapsed time in microseconds, or -1 on timeout. + * + * @param gpio GPIO pin to monitor + * @param level Desired logic level (true = HIGH, false = LOW) + * @param timeout_us Maximum wait in microseconds + * @return int64_t Elapsed microseconds, or -1 on timeout + */ +static int64_t _wait_for_level(unsigned int gpio, bool level, uint32_t timeout_us) { absolute_time_t start = get_absolute_time(); while (gpio_get(gpio) != level) { - if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) { + if (absolute_time_diff_us(start, get_absolute_time()) > (int64_t)timeout_us) return -1; - } } return absolute_time_diff_us(start, get_absolute_time()); } + +/** + * @brief Wait for the NEC 9 ms leader pulse and 4.5 ms space + * + * @return bool true if a valid leader was detected, false on timeout + */ +static bool _wait_leader(void) { + if (_wait_for_level(ir_pin, 0, 150000) < 0) return false; + int64_t t = _wait_for_level(ir_pin, 1, 12000); + if (t < 8000 || t > 10000) return false; + t = _wait_for_level(ir_pin, 0, 7000); + if (t < 3500 || t > 5000) return false; + return true; +} + + +/** + * @brief Read a single NEC-encoded bit from the IR receiver + * + * Measures the mark/space timing and shifts the result into the + * appropriate byte of the data array. + * + * @param data 4-byte array accumulating received bits + * @param i Bit index (0-31) + * @return bool true on success, false on timeout or protocol error + */ +static bool _read_nec_bit(uint8_t *data, int i) { + if (_wait_for_level(ir_pin, 1, 1000) < 0) return false; + int64_t t = _wait_for_level(ir_pin, 0, 2500); + if (t < 200) return false; + int byte_idx = i / 8; + int bit_idx = i % 8; + if (t > 1200) data[byte_idx] |= (1 << bit_idx); + return true; +} + + +/** + * @brief Read all 32 data bits of an NEC frame + * + * @param data 4-byte array filled with the received address and command + * @return bool true if all 32 bits were read, false on timeout + */ +static bool _read_32_bits(uint8_t *data) { + for (int i = 0; i < 32; ++i) + if (!_read_nec_bit(data, i)) return false; + return true; +} + + +/** + * @brief Validate an NEC frame and extract the command byte + * + * Checks that the address and command pairs are bitwise-inverted. + * + * @param data 4-byte NEC frame (addr, ~addr, cmd, ~cmd) + * @return int Command byte (0-255) on success, -1 on validation failure + */ +static int _validate_nec_frame(const uint8_t *data) { + if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) + return data[2]; + return -1; +} + + void ir_init(uint8_t pin) { ir_pin = pin; gpio_init(pin); @@ -52,29 +127,10 @@ void ir_init(uint8_t pin) { gpio_pull_up(pin); } + int ir_getkey(void) { - // leader low (~9 ms) - if (wait_for_level(ir_pin, 0, 150000) < 0) return -1; - - int64_t t = wait_for_level(ir_pin, 1, 12000); - if (t < 8000 || t > 10000) return -1; - - t = wait_for_level(ir_pin, 0, 7000); - if (t < 3500 || t > 5000) return -1; - + if (!_wait_leader()) return -1; uint8_t data[4] = {0, 0, 0, 0}; - for (int i = 0; i < 32; ++i) { - if (wait_for_level(ir_pin, 1, 1000) < 0) return -1; - t = wait_for_level(ir_pin, 0, 2500); - if (t < 200) return -1; - int byte_idx = i / 8; - int bit_idx = i % 8; - if (t > 1200) data[byte_idx] |= (1 << bit_idx); // logical '1' - } - - // Validate address/data inverted pairs - if ((uint8_t)(data[0] + data[1]) == 0xFF && (uint8_t)(data[2] + data[3]) == 0xFF) { - return data[2]; - } - return -1; + if (!_read_32_bits(data)) return -1; + return _validate_nec_frame(data); } diff --git a/drivers/0x0b_spi/0x0b_spi.c b/drivers/0x0b_spi/0x0b_spi.c index 0b71d19..be7b617 100644 --- a/drivers/0x0b_spi/0x0b_spi.c +++ b/drivers/0x0b_spi/0x0b_spi.c @@ -50,23 +50,41 @@ #define PIN_SCK 18 #define PIN_MOSI 19 + +/** + * @brief Perform one SPI loopback transfer and print the result + * + * Asserts chip-select, transfers the buffer, deasserts chip-select, + * prints TX and RX data over UART, then clears the receive buffer. + * + * @param tx_buf Transmit buffer + * @param rx_buf Receive buffer (cleared after printing) + * @param len Number of bytes to transfer + */ +static void _loopback_transfer(const uint8_t *tx_buf, uint8_t *rx_buf, size_t len) { + spi_driver_cs_select(PIN_CS); + spi_driver_transfer(SPI_PORT, tx_buf, rx_buf, len); + spi_driver_cs_deselect(PIN_CS); + printf("TX: %s\r\n", tx_buf); + printf("RX: %s\r\n\r\n", rx_buf); + memset(rx_buf, 0, len); + sleep_ms(1000); +} + + +/** + * @brief Application entry point for the SPI loopback demo + * + * Initializes SPI0 in master mode and continuously performs + * full-duplex transfers with MOSI wired to MISO for loopback. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); - spi_driver_init(SPI_PORT, PIN_MOSI, PIN_MISO, PIN_SCK, PIN_CS, SPI_BAUD_HZ); - const uint8_t tx_buf[] = "SPI loopback OK"; uint8_t rx_buf[sizeof(tx_buf)] = {0}; - - while (true) { - spi_driver_cs_select(PIN_CS); - spi_driver_transfer(SPI_PORT, tx_buf, rx_buf, sizeof(tx_buf)); - spi_driver_cs_deselect(PIN_CS); - - printf("TX: %s\r\n", tx_buf); - printf("RX: %s\r\n\r\n", rx_buf); - - memset(rx_buf, 0, sizeof(rx_buf)); - sleep_ms(1000); - } + while (true) + _loopback_transfer(tx_buf, rx_buf, sizeof(tx_buf)); } diff --git a/drivers/0x0b_spi/spi.c b/drivers/0x0b_spi/spi.c index baba27f..a4511c5 100644 --- a/drivers/0x0b_spi/spi.c +++ b/drivers/0x0b_spi/spi.c @@ -31,34 +31,54 @@ #include "pico/stdlib.h" #include "hardware/spi.h" -static spi_inst_t *get_spi_inst(uint8_t port) { +/** + * @brief Map an SPI port number to its hardware instance pointer + * + * @param port SPI port number (0 for spi0, 1 for spi1) + * @return spi_inst_t* Pointer to the corresponding SPI hardware instance + */ +static spi_inst_t *_get_spi_inst(uint8_t port) { return port == 0 ? spi0 : spi1; } -void spi_driver_init(uint8_t port, uint32_t mosi, uint32_t miso, - uint32_t sck, uint32_t cs, uint32_t baud_hz) { - spi_inst_t *spi = get_spi_inst(port); - spi_init(spi, baud_hz); +/** + * @brief Assign GPIO alternate functions for the SPI data and clock pins + * + * @param mosi GPIO pin for MOSI + * @param miso GPIO pin for MISO + * @param sck GPIO pin for SCK + */ +static void _setup_spi_pins(uint32_t mosi, uint32_t miso, uint32_t sck) { gpio_set_function(mosi, GPIO_FUNC_SPI); gpio_set_function(miso, GPIO_FUNC_SPI); gpio_set_function(sck, GPIO_FUNC_SPI); +} + +void spi_driver_init(uint8_t port, uint32_t mosi, uint32_t miso, + uint32_t sck, uint32_t cs, uint32_t baud_hz) { + spi_inst_t *spi = _get_spi_inst(port); + spi_init(spi, baud_hz); + _setup_spi_pins(mosi, miso, sck); gpio_init(cs); gpio_set_dir(cs, GPIO_OUT); gpio_put(cs, 1); } + void spi_driver_cs_select(uint32_t cs) { gpio_put(cs, 0); } + void spi_driver_cs_deselect(uint32_t cs) { gpio_put(cs, 1); } + void spi_driver_transfer(uint8_t port, const uint8_t *tx, uint8_t *rx, uint32_t len) { - spi_inst_t *spi = get_spi_inst(port); + spi_inst_t *spi = _get_spi_inst(port); spi_write_read_blocking(spi, tx, rx, (size_t)len); } diff --git a/drivers/0x0c_multicore/0x0c_multicore.c b/drivers/0x0c_multicore/0x0c_multicore.c index b68c6b2..98f1f01 100644 --- a/drivers/0x0c_multicore/0x0c_multicore.c +++ b/drivers/0x0c_multicore/0x0c_multicore.c @@ -41,24 +41,48 @@ #include "pico/stdlib.h" #include "multicore.h" -static void core1_main(void) { +/** + * @brief Core 1 entry point: receive a value and return it incremented + * + * Blocks on the FIFO, adds one to each received value, and pushes + * the result back to core 0. + */ +static void _core1_main(void) { while (true) { uint32_t value = multicore_driver_pop(); multicore_driver_push(value + 1u); } } + +/** + * @brief Send the counter to core 1 and print the round-trip result + * + * @param counter Pointer to the running counter (post-incremented) + */ +static void _send_and_print(uint32_t *counter) { + multicore_driver_push(*counter); + uint32_t response = multicore_driver_pop(); + printf("core0 sent: %lu, core1 returned: %lu\r\n", + (unsigned long)*counter, (unsigned long)response); + (*counter)++; + sleep_ms(1000); +} + + +/** + * @brief Application entry point for the multicore FIFO demo + * + * Launches core 1 and continuously sends an incrementing counter + * through the inter-core FIFO, printing both sent and returned + * values over UART every second. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); - multicore_driver_launch(core1_main); - + multicore_driver_launch(_core1_main); uint32_t counter = 0; - while (true) { - multicore_driver_push(counter); - uint32_t response = multicore_driver_pop(); - printf("core0 sent: %lu, core1 returned: %lu\r\n", - (unsigned long)counter, (unsigned long)response); - counter++; - sleep_ms(1000); - } + while (true) + _send_and_print(&counter); } diff --git a/drivers/0x0c_multicore/multicore.c b/drivers/0x0c_multicore/multicore.c index d0a93f9..953e909 100644 --- a/drivers/0x0c_multicore/multicore.c +++ b/drivers/0x0c_multicore/multicore.c @@ -34,10 +34,12 @@ void multicore_driver_launch(void (*core1_entry)(void)) { multicore_launch_core1(core1_entry); } + void multicore_driver_push(uint32_t data) { multicore_fifo_push_blocking(data); } + uint32_t multicore_driver_pop(void) { return multicore_fifo_pop_blocking(); } diff --git a/drivers/0x0d_timer/0x0d_timer.c b/drivers/0x0d_timer/0x0d_timer.c index 400df78..1262086 100644 --- a/drivers/0x0d_timer/0x0d_timer.c +++ b/drivers/0x0d_timer/0x0d_timer.c @@ -40,17 +40,28 @@ #include "pico/stdlib.h" #include "timer.h" -static bool heartbeat_callback(void) { +/** + * @brief Heartbeat timer callback that prints a message over UART + * + * @return bool true to keep the repeating timer active + */ +static bool _heartbeat_callback(void) { printf("Timer heartbeat\r\n"); return true; } + +/** + * @brief Application entry point for the repeating timer demo + * + * Starts a 1-second repeating timer whose callback prints a + * heartbeat message over UART. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); - - timer_driver_start(1000, heartbeat_callback); - - while (true) { + timer_driver_start(1000, _heartbeat_callback); + while (true) tight_loop_contents(); - } } diff --git a/drivers/0x0d_timer/timer.c b/drivers/0x0d_timer/timer.c index f8ce692..fad6726 100644 --- a/drivers/0x0d_timer/timer.c +++ b/drivers/0x0d_timer/timer.c @@ -34,29 +34,34 @@ static repeating_timer_t g_timer; static bool g_timer_active = false; static timer_driver_callback_t g_user_callback = NULL; -static bool timer_shim(repeating_timer_t *rt) { + +/** + * @brief Internal repeating-timer callback that forwards to the user callback + * + * @param rt Unused repeating-timer handle provided by the SDK + * @return bool true to keep the timer running, false to cancel + */ +static bool _timer_shim(repeating_timer_t *rt) { (void)rt; - if (g_user_callback) { + if (g_user_callback) return g_user_callback(); - } return false; } + void timer_driver_start(int32_t period_ms, timer_driver_callback_t callback) { if (g_timer_active) { cancel_repeating_timer(&g_timer); g_timer_active = false; } - g_user_callback = callback; - g_timer_active = add_repeating_timer_ms(period_ms, timer_shim, NULL, &g_timer); + g_timer_active = add_repeating_timer_ms(period_ms, _timer_shim, NULL, &g_timer); } -void timer_driver_cancel(void) { - if (!g_timer_active) { - return; - } +void timer_driver_cancel(void) { + if (!g_timer_active) + return; cancel_repeating_timer(&g_timer); g_timer_active = false; } diff --git a/drivers/0x0e_watchdog/0x0e_watchdog.c b/drivers/0x0e_watchdog/0x0e_watchdog.c index 0b9d556..9a99348 100644 --- a/drivers/0x0e_watchdog/0x0e_watchdog.c +++ b/drivers/0x0e_watchdog/0x0e_watchdog.c @@ -41,21 +41,40 @@ #include "pico/stdlib.h" #include "watchdog.h" +/** + * @brief Print whether the system booted normally or from a watchdog reset + */ +static void _print_reset_reason(void) { + if (watchdog_driver_caused_reboot()) + printf("System rebooted by watchdog timeout\r\n"); + else + printf("Normal power-on reset\r\n"); +} + + +/** + * @brief Feed the watchdog and log over UART, then wait 1 second + */ +static void _feed_and_report(void) { + watchdog_driver_feed(); + printf("Watchdog fed\r\n"); + sleep_ms(1000); +} + + +/** + * @brief Application entry point for the watchdog demo + * + * Enables the watchdog with a 3-second timeout and feeds it every + * 1 second. Reports the reset reason on startup. + * + * @return int Does not return + */ int main(void) { stdio_init_all(); - - if (watchdog_driver_caused_reboot()) { - printf("System rebooted by watchdog timeout\r\n"); - } else { - printf("Normal power-on reset\r\n"); - } - + _print_reset_reason(); watchdog_driver_enable(3000); printf("Watchdog enabled (3s timeout). Feeding every 1s...\r\n"); - - while (true) { - watchdog_driver_feed(); - printf("Watchdog fed\r\n"); - sleep_ms(1000); - } + while (true) + _feed_and_report(); } diff --git a/drivers/0x0e_watchdog/watchdog.c b/drivers/0x0e_watchdog/watchdog.c index fb677de..0ae52ab 100644 --- a/drivers/0x0e_watchdog/watchdog.c +++ b/drivers/0x0e_watchdog/watchdog.c @@ -34,10 +34,12 @@ void watchdog_driver_enable(uint32_t timeout_ms) { watchdog_enable(timeout_ms, true); } + void watchdog_driver_feed(void) { watchdog_update(); } + bool watchdog_driver_caused_reboot(void) { return watchdog_caused_reboot(); } diff --git a/drivers/0x0f_flash/0x0f_flash.c b/drivers/0x0f_flash/0x0f_flash.c index 2231f57..980d9ac 100644 --- a/drivers/0x0f_flash/0x0f_flash.c +++ b/drivers/0x0f_flash/0x0f_flash.c @@ -44,22 +44,44 @@ #define FLASH_TARGET_OFFSET (FLASH_DRIVER_SIZE_BYTES - FLASH_DRIVER_SECTOR_SIZE) #define FLASH_WRITE_LEN FLASH_DRIVER_PAGE_SIZE -int main(void) { - stdio_init_all(); +/** + * @brief Fill a write buffer with 0xFF and copy the demo string into it + * + * @param buf Destination buffer + * @param buf_size Size of the buffer in bytes + */ +static void _prepare_write_buf(uint8_t *buf, size_t buf_size) { + memset(buf, 0xFF, buf_size); + const char *msg = "Embedded Hacking flash driver demo"; + memcpy(buf, msg, strlen(msg)); +} + + +/** + * @brief Write the demo string to flash and print the read-back result + */ +static void _write_and_verify(void) { static uint8_t write_buf[FLASH_WRITE_LEN]; static uint8_t read_buf[FLASH_WRITE_LEN]; - - memset(write_buf, 0xFF, sizeof(write_buf)); - const char *msg = "Embedded Hacking flash driver demo"; - memcpy(write_buf, msg, strlen(msg)); - + _prepare_write_buf(write_buf, sizeof(write_buf)); flash_driver_write(FLASH_TARGET_OFFSET, write_buf, FLASH_WRITE_LEN); flash_driver_read(FLASH_TARGET_OFFSET, read_buf, FLASH_WRITE_LEN); - printf("Flash readback: %s\r\n", read_buf); - - while (true) { - tight_loop_contents(); - } +} + + +/** + * @brief Application entry point for the on-chip flash demo + * + * Writes a demo string to the last flash sector, reads it back, + * and prints the result over UART. + * + * @return int Does not return + */ +int main(void) { + stdio_init_all(); + _write_and_verify(); + while (true) + tight_loop_contents(); } diff --git a/drivers/0x0f_flash/flash.c b/drivers/0x0f_flash/flash.c index ad010cb..6a31e40 100644 --- a/drivers/0x0f_flash/flash.c +++ b/drivers/0x0f_flash/flash.c @@ -40,6 +40,7 @@ void flash_driver_write(uint32_t flash_offset, const uint8_t *data, uint32_t len restore_interrupts(ints); } + void flash_driver_read(uint32_t flash_offset, uint8_t *out, uint32_t len) { const uint8_t *flash_target_contents = (const uint8_t *)(XIP_BASE + flash_offset); memcpy(out, flash_target_contents, len);