Pro testovaní přerušení jsem potřeboval generátor obdélníkových puslů s nastavitelnou frekvencí a střídou pulsů. Nechtělo se mi čekat, až mi přijde poštou, tak jsem si ho sestrojil. Potřeboval jsem Pico, LCD displej 1602, 4 tlačítka, 2 rezistory 12k, breadboard a několik drátků. Displej jsem už měl naprogramovaný z dřívějška, takže stačilo přidat do programu jeden céčkový zdroják a zapojit displej. Žlutými tlačítky měním frekvenci po 1 Hz. Červenými tlačítky měním střídu po 1%. Stav tlačítek je zpracováván pomocí přerušení.

Pico vlevo nahoře je pro testování, Pico vpravo nahoře je datový analyzátor a Pico vpravo dole je generátor signálu.
Zdrojové soubory
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(generator)
pico_sdk_init()
add_executable(generator
generator1.c
lcd_czech_chars.c
)
target_link_libraries(generator
pico_stdlib
hardware_pwm
hardware_i2c
)
pico_enable_stdio_usb(generator 1)
pico_enable_stdio_uart(generator 0)
pico_add_extra_outputs(generator)
/* generator.c
* Generator PWM pulsu s ovladanim RPi Pico
* (c) Jirka Chráska 2024, <jirka@lixis.cz> All rights reserved.
*
* BSD licence
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pwm.h"
#include "hardware/gpio.h"
#include "lcd_czech_chars.h"
const uint FREQ_PLUS = 12; // tlačítko pro přidání frekvence
const uint FREQ_MINUS = 13; // tlačítko pro ubrání frekvence
const uint DUTY_PLUS = 14; // tlačítko pro přidání střídy
const uint DUTY_MINUS = 15; // tlačítko pro ubrání střídy
const uint PWM_PIN = 16; // GPIO pin, kde se generují pulsy
static uint freq = 1000;
static uint duty = 50;
static bool zmena = false;
uint32_t pwm_set_freq_duty( uint slice_num, uint chan, uint32_t f, int d)
{
uint32_t clock = 125000000;
uint32_t divider16 = clock / f / 4096 + (clock % (f * 4096) != 0);
if( divider16 / 16 == 0) {
divider16 = 16;
}
uint32_t wrap = clock * 16 / divider16 / f - 1;
pwm_set_clkdiv_int_frac( slice_num, divider16/16, divider16 & 0xf );
pwm_set_wrap( slice_num, wrap );
pwm_set_chan_level( slice_num, chan, wrap * d/100 );
return wrap;
}
void MyIRQHandler( uint gpio, uint32_t events )
{
switch( gpio ) {
case FREQ_PLUS: freq++; zmena=true; break;
case FREQ_MINUS: if(freq > 0) {freq--; zmena=true;} break;
case DUTY_PLUS: if(duty < 101) {duty++; zmena=true;} break;
case DUTY_MINUS: if(duty > 0) {duty--; zmena=true; } break;
default: zmena=false; return;
}
}
int udelej_zmeny()
{
char buffer[40];
// zmena pwm
uint slice = pwm_gpio_to_slice_num (PWM_PIN);
uint channel = pwm_gpio_to_channel (PWM_PIN);
pwm_set_freq_duty(slice, channel, freq, duty);
pwm_set_enabled (slice, true);
// zobraz na displeji
snprintf(buffer,40,"Freq: %5d Hz", freq);
printf("%s ", buffer);
lcd_home();
lcd_set_cursor(0,0);
cz_print(buffer);
snprintf(buffer,40,"Duty: %3d %%", duty);
lcd_set_cursor(1,0);
cz_print(buffer);
printf("%s \n", buffer);
zmena = false;
}
int main(void)
{
// konfigurace debugování přes USB
stdio_init_all();
sleep_ms(2000);
// konfigurace displeje
init_gpio();
lcd_characters_init();
lcd_init();
lcd_clear();
// pwm
gpio_set_function(PWM_PIN, GPIO_FUNC_PWM);
zmena = true;
udelej_zmeny();
// frekvence přidat
gpio_set_function( FREQ_PLUS, GPIO_FUNC_SIO );
gpio_set_dir( FREQ_PLUS, false );
gpio_pull_up( FREQ_PLUS );
gpio_set_irq_enabled_with_callback( FREQ_PLUS, GPIO_IRQ_EDGE_FALL, true, &MyIRQHandler );
// frekvence ubrat
gpio_set_function( FREQ_MINUS, GPIO_FUNC_SIO );
gpio_set_dir( FREQ_MINUS, false );
gpio_pull_up( FREQ_MINUS );
gpio_set_irq_enabled_with_callback( FREQ_MINUS, GPIO_IRQ_EDGE_FALL, true, &MyIRQHandler );
// duty přidat
gpio_set_function( DUTY_PLUS, GPIO_FUNC_SIO );
gpio_set_dir( DUTY_PLUS, false );
gpio_pull_up( DUTY_PLUS );
gpio_set_irq_enabled_with_callback( DUTY_PLUS, GPIO_IRQ_EDGE_FALL, true, &MyIRQHandler );
// duty ubrat
gpio_set_function( DUTY_MINUS, GPIO_FUNC_SIO );
gpio_set_dir( DUTY_MINUS, false );
gpio_pull_up( DUTY_MINUS );
gpio_set_irq_enabled_with_callback( DUTY_MINUS, GPIO_IRQ_EDGE_FALL, true, &MyIRQHandler );
// nekonečná smyčka
while(1){
if( zmena ) {
udelej_zmeny();
}
sleep_ms(200);
}
}
Problémy
Řízení pomocí tlačítek s interupty není moc dobré, protože při zákmitu tlačítka se vygeneruje několik přerušení a veličina poskočí o víc, než o jedničku. Budu to muset předělat na řízení pomocí čtecí smyčky, protože potom budu moci delším stisknutím tlačítka měnit veličinu o víc než jedna a plynule.
Možná bude dobré si uložit nastavenou frekvenci a střídu do flash paměti na Picu, abych ji nemusel při každém zapnutí nastavovat znovu. Jak to udělat je popsáno zde: Čtení a zápis dat na vestavěnou flash paměť Raspberry Pi Pico
$ objdump --all generator.elf | grep flash_binary_end
10008e60 g .ARM.attributes 00000000 __flash_binary_end