mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-24 00:14:04 +02:00
style: enforce coding standard — headers, @brief on #define, Doxygen on statics, remove _ prefix, max 8 lines per function
This commit is contained in:
@@ -1,44 +1,111 @@
|
||||
/**
|
||||
* @file 0x0020_dynamic-conditionals.c
|
||||
* @brief Dynamic conditionals: UART input drives if/else and switch with servo
|
||||
* @author Kevin Thomas
|
||||
* @date 2025
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2025 Kevin Thomas
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* Demonstrates dynamic (runtime) conditionals using UART input via getchar().
|
||||
* Reads a character, evaluates it with if/else and switch/case, and controls
|
||||
* a servo on GPIO6 based on the input ('1' or '2').
|
||||
*
|
||||
* Wiring:
|
||||
* GPIO6 -> Servo signal wire (orange/white)
|
||||
* 5V -> Servo VCC (red)
|
||||
* GND -> Servo GND (brown/black)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "servo.h"
|
||||
|
||||
/** @brief GPIO pin number for the servo */
|
||||
#define SERVO_GPIO 6
|
||||
|
||||
/**
|
||||
* @brief Evaluate choice with if/else conditional and print result
|
||||
*
|
||||
* @details Compares against hex ASCII values 0x31 ('1') and 0x32 ('2').
|
||||
*
|
||||
* @param choice character value received from UART
|
||||
* @retval None
|
||||
*/
|
||||
static void eval_if_else(uint8_t choice) {
|
||||
if (choice == 0x31) {
|
||||
printf("1\r\n");
|
||||
} else if (choice == 0x32) {
|
||||
printf("2\r\n");
|
||||
} else {
|
||||
printf("??\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sweep servo from start angle to end angle with a pause
|
||||
*
|
||||
* @details Sets the servo to start, waits 500 ms, sets to end,
|
||||
* waits 500 ms.
|
||||
*
|
||||
* @param label text to print before sweeping
|
||||
* @param start starting angle in degrees
|
||||
* @param end ending angle in degrees
|
||||
* @retval None
|
||||
*/
|
||||
static void sweep_servo(const char *label, float start, float end) {
|
||||
printf("%s\r\n", label);
|
||||
servo_set_angle(start);
|
||||
sleep_ms(500);
|
||||
servo_set_angle(end);
|
||||
sleep_ms(500);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process choice with switch/case and drive servo accordingly
|
||||
*
|
||||
* @details On '1', sweeps servo 0->180; on '2', sweeps 180->0;
|
||||
* otherwise prints unknown.
|
||||
*
|
||||
* @param choice character value received from UART
|
||||
* @retval None
|
||||
*/
|
||||
static void process_servo_command(uint8_t choice) {
|
||||
switch (choice) {
|
||||
case '1': sweep_servo("one", 0.0f, 180.0f); break;
|
||||
case '2': sweep_servo("two", 180.0f, 0.0f); break;
|
||||
default: printf("??\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
stdio_init_all();
|
||||
|
||||
uint8_t choice = 0;
|
||||
|
||||
servo_init(SERVO_GPIO);
|
||||
|
||||
while (true) {
|
||||
choice = getchar();
|
||||
|
||||
if (choice == 0x31) {
|
||||
printf("1\r\n");
|
||||
} else if (choice == 0x32) {
|
||||
printf("2\r\n");
|
||||
} else {
|
||||
printf("??\r\n");
|
||||
}
|
||||
|
||||
switch (choice) {
|
||||
case '1':
|
||||
printf("one\r\n");
|
||||
servo_set_angle(0.0f);
|
||||
sleep_ms(500);
|
||||
servo_set_angle(180.0f);
|
||||
sleep_ms(500);
|
||||
break;
|
||||
case '2':
|
||||
printf("two\r\n");
|
||||
servo_set_angle(180.0f);
|
||||
sleep_ms(500);
|
||||
servo_set_angle(0.0f);
|
||||
sleep_ms(500);
|
||||
break;
|
||||
default:
|
||||
printf("??\r\n");
|
||||
}
|
||||
eval_if_else(choice);
|
||||
process_servo_command(choice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,46 +32,66 @@
|
||||
#include "hardware/pwm.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
// Default servo pulse range (SG90 typical)
|
||||
/** @brief Default minimum pulse width in microseconds */
|
||||
static const uint16_t SERVO_DEFAULT_MIN_US = 1000;
|
||||
/** @brief Default maximum pulse width in microseconds */
|
||||
static const uint16_t SERVO_DEFAULT_MAX_US = 2000;
|
||||
|
||||
// internal state
|
||||
/** @brief GPIO pin assigned to the servo */
|
||||
static uint8_t servo_pin = 0;
|
||||
/** @brief PWM hardware slice for the servo pin */
|
||||
static uint servo_slice = 0;
|
||||
/** @brief PWM channel within the servo slice */
|
||||
static uint servo_chan = 0;
|
||||
static uint32_t servo_wrap = 20000 - 1; // wrap to map microseconds for 50Hz
|
||||
/** @brief PWM counter wrap value for 50 Hz servo */
|
||||
static uint32_t servo_wrap = 20000 - 1;
|
||||
/** @brief Servo PWM frequency in Hz */
|
||||
static float servo_hz = 50.0f;
|
||||
/** @brief Flag indicating servo has been initialized */
|
||||
static bool servo_initialized = false;
|
||||
|
||||
// Convert microsecond pulse to PWM level based on wrap and frequency
|
||||
/**
|
||||
* @brief Convert a pulse width in microseconds to a PWM counter level
|
||||
*
|
||||
* Uses the configured PWM wrap and servo frequency to map pulse time
|
||||
* into the channel compare value expected by the PWM hardware.
|
||||
*
|
||||
* @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
|
||||
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_pin == 0) 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);
|
||||
@@ -81,9 +101,7 @@ void servo_set_pulse_us(uint16_t pulse_us) {
|
||||
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);
|
||||
}
|
||||
#
|
||||
}
|
||||
Reference in New Issue
Block a user