INF2004_Labs/lab_4/lab_4.c

121 lines
3.4 KiB
C

/**
* To configure a PWM signal at 20 Hz with a 50% duty cycle
* on GP2 and feed it into an ADC at GP26 while
* sampling the ADC every 25 ms,
* you must use the Raspberry Pi Pico and its Pico C SDK.
*
* You will need to use a jumper wire to connect GP2 to GP26.
* You may use a timer interrupt.
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "hardware/adc.h"
#define PWM_PIN 2
#define PWM_FREQ 20
#define PWM_DUTY 50
#define ADC_PIN 26
#define ADC_FREQ 25
/**
* 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();
uint64_t time = time_us_64();
printf("%02d:%02d:%02d:%03d -> ADC value: %d\n",
(int) (time / 3600000000),
(int) (time / 60000000) % 60,
(int) (time / 1000000) % 60,
(int) (time / 1000) % 1000,
result
);
return true;
}
/**
* 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;
// 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
);
}
int main () {
stdio_init_all();
struct repeating_timer timer;
setup(&timer);
while (true)
{
tight_loop_contents();
}
}