mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-16 13:19:14 +02:00
Updated Drivers
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+30
-12
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-8
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-19
@@ -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);
|
||||
|
||||
+24
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
+42
-20
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+100
-32
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+83
-27
@@ -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);
|
||||
}
|
||||
|
||||
+32
-14
@@ -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));
|
||||
}
|
||||
|
||||
+26
-6
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user