diff --git a/lab_4/lab_4.c b/lab_4/lab_4.c index 52f3f30..07a265b 100644 --- a/lab_4/lab_4.c +++ b/lab_4/lab_4.c @@ -16,27 +16,16 @@ #define PWM_PIN 2 #define PWM_FREQ 20 -#define PWM_DUTY 0.5 +#define PWM_DUTY 50 #define ADC_PIN 26 #define ADC_FREQ 25 -void setup () { - gpio_set_function(PWM_PIN, GPIO_FUNC_PWM); - uint slice_num = pwm_gpio_to_slice_num(PWM_PIN); - - pwm_set_wrap(slice_num, 100); - // Setup PWM with 20 Hz frequency and 50% duty cycle - pwm_set_chan_level(slice_num, PWM_CHAN_A, PWM_DUTY * 100); - pwm_set_enabled(slice_num, true); - pwm_set_clkdiv(slice_num, 1); - - // Set up ADC - adc_init(); - adc_gpio_init(ADC_PIN); - adc_select_input(0); -} - +/** + * Callback function for repeating timer to read ADC value + * @param t The timer that triggered the callback + * @return True to keep the timer running, false to stop it + */ bool repeating_timer_callback (struct repeating_timer *t) { // Read ADC value uint16_t result = adc_read(); @@ -52,18 +41,77 @@ bool repeating_timer_callback (struct repeating_timer *t) { return true; } -int main () { - stdio_init_all(); - setup(); +/** + * Helper function to calculate the clock divider and wrap value for PWM + * Based on: https://www.i-programmer.info/programming/hardware/14849-the-pico-in-c-basic-pwm.html + * + * RP2040 clock is 125 MHz, PWM Counter is 16-bit + * Clock divider is 16-bit, with 12-bit integer and 4-bit fraction + * @param slice_num PWM slice number + * @param channel PWM channel + * @param frequency PWM frequency + * @param duty_cycle PWM duty cycle + * @return PWM wrap value + */ +uint32_t pwm_set_freq_duty (uint slice_num, + uint channel, uint32_t frequency, int duty_cycle) { + uint32_t clock = 125000000; - struct repeating_timer timer; + // divider = Ceil(16 * clock / 65536 * frequency) + remainder + // = Ceil(clock / frequency / 4096) + remainder + uint32_t divider16 = clock / frequency / 4096 + + (clock % (frequency * 4096) != + 0); // Round up if less than 1 + + // If divider is less than 16, set it to 16 (16-bit counter) + if (divider16 / 16 == 0) + { + divider16 = 16; + } + + // Wrap value is 16 bits, so max value is 65535 (2^16 - 1) + uint32_t wrap = clock * 16 / divider16 / frequency - 1; + + // Set clock divider and wrap value + pwm_set_clkdiv_int_frac(slice_num, divider16 / 16, + divider16 & 0xF); + pwm_set_wrap(slice_num, wrap); + + // Level = wrap * duty_cycle(as fraction) + // = wrap * duty_cycle / 100 + pwm_set_chan_level(slice_num, channel, wrap * duty_cycle / 100); + return wrap; +} + +/** + * Setup Function for PWM and ADC + * @param timer Repeating timer for ADC + */ +void setup (struct repeating_timer *timer) { + // PWM setup + gpio_set_function(PWM_PIN, GPIO_FUNC_PWM); + uint slice_num = pwm_gpio_to_slice_num(PWM_PIN); + uint channel = pwm_gpio_to_channel(PWM_PIN); + pwm_set_freq_duty(slice_num, channel, PWM_FREQ, PWM_DUTY); + pwm_set_enabled(slice_num, true); + + // ADC setup + adc_init(); + adc_gpio_init(ADC_PIN); + adc_select_input(0); // Setup timer interrupt add_repeating_timer_ms(ADC_FREQ, repeating_timer_callback, NULL, - &timer + timer ); +} + +int main () { + stdio_init_all(); + struct repeating_timer timer; + setup(&timer); while (true) {