Initial commit: most infrastructure already done

- 7-segment display controlled via PIO and DMA
- buzzer works with PWM
- millisecond trigger interrupt works
This commit is contained in:
Thomas Kolb 2024-05-09 23:56:32 +02:00
commit a4e935692a
11 changed files with 440 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/
.cache/

33
CMakeLists.txt Normal file
View file

@ -0,0 +1,33 @@
cmake_minimum_required (VERSION 3.13)
include(pico_sdk_import.cmake)
project (zam_power_timer VERSION 0.1 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(${CMAKE_PROJECT_NAME}
src/main.c
src/sseg.c
src/sseg.h
src/pinout.h
src/sseg.pio
)
pico_generate_pio_header(zam_power_timer ${CMAKE_CURRENT_LIST_DIR}/src/sseg.pio)
pico_enable_stdio_usb(${CMAKE_PROJECT_NAME} 1)
pico_enable_stdio_uart(${CMAKE_PROJECT_NAME} 0)
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
target_link_libraries(${CMAKE_PROJECT_NAME}
pico_stdlib
hardware_pwm
hardware_timer
hardware_irq
hardware_pio
hardware_dma)

1
compile_commands.json Symbolic link
View file

@ -0,0 +1 @@
build/compile_commands.json

16
doc/sseg_codes.txt Normal file
View file

@ -0,0 +1,16 @@
7654 3210 Hex
ABCDEFGP PCBA DEFG
Z: 11011010 0011 1101 0x3C
A: 11101110 0111 0111 0x77
M: 11101100 0111 0110 0x76
0: 11111100 0111 1110 0x7E
1: 01100000 0110 0000 0x60
2: 11011010 0011 1101 0x3D
3: 11110010 0111 1001 0x79
4: 01100110 0110 0011 0x63
5: 10110110 0101 1011 0x5B
6: 10111110 0101 1111 0x5F
7: 11100000 0111 0000 0x70
8: 11111110 0111 1111 0x7F
9: 11110110 0111 1011 0x7B

8
make.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
export PICO_SDK_PATH=/home/thomas/Software/rpi-pico/pico-sdk
mkdir -p build
cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..
make $@

73
pico_sdk_import.cmake Normal file
View file

@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

118
src/main.c Normal file
View file

@ -0,0 +1,118 @@
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/pwm.h"
#include "hardware/irq.h"
#include "sseg.h"
#include "pinout.h"
#include <pico/stdlib.h>
const uint8_t all_gpios[] = {
OUTPUT_ENABLE_PIN,
};
static volatile bool m_ms_triggered;
static uint32_t m_next_ms_trigger;
#define MS_TRIG_ALARM_NUM 0
#define MS_TRIG_ALARM_IRQ TIMER_IRQ_0
static void arm_ms_trig_alarm(void)
{
m_next_ms_trigger += 1000; // microseconds
timer_hw->alarm[MS_TRIG_ALARM_NUM] = m_next_ms_trigger;
}
static void alarm_irq(void)
{
// Clear the alarm irq
hw_clear_bits(&timer_hw->intr, 1u << MS_TRIG_ALARM_NUM);
// set the next alarm
arm_ms_trig_alarm();
// Assume alarm 0 has fired
m_ms_triggered = true;
}
static void configure_ms_trig_alarm(void)
{
m_ms_triggered = false;
hw_set_bits(&timer_hw->inte, 1u << MS_TRIG_ALARM_NUM);
irq_set_exclusive_handler(MS_TRIG_ALARM_IRQ, alarm_irq);
irq_set_enabled(MS_TRIG_ALARM_IRQ, true);
m_next_ms_trigger = timer_hw->timelr + 10000;
}
int main()
{
//stdio_init_all();
//printf("Hello, world!\n");
bool power_switch_state = false;
for(size_t i = 0; i < sizeof(all_gpios); i++) {
gpio_init(all_gpios[i]);
gpio_put(all_gpios[i], false);
gpio_set_dir(all_gpios[i], true);
}
gpio_put(OUTPUT_ENABLE_PIN, true);
// set up PWM for 50 kHz @ 512 steps resolution
uint slice_num = pwm_gpio_to_slice_num(BUZZER_PIN);
pwm_set_clkdiv_int_frac(slice_num, 244, (14 << 4) / 100); // 125 MHz / 244.14 / 512 = 1 kHz
gpio_set_function(BUZZER_PIN, GPIO_FUNC_PWM);
pwm_set_wrap(slice_num, 512);
pwm_set_chan_level(slice_num, PWM_CHAN_B, 1 * 512 / 100);
pwm_set_enabled(slice_num, true);
sleep_ms(200);
pwm_set_enabled(slice_num, false);
pwm_set_clkdiv_int_frac(slice_num, 122, (7 << 4) / 100); // 125 MHz / 122.07 / 512 = 2 kHz
pwm_set_enabled(slice_num, true);
sleep_ms(200);
pwm_set_enabled(slice_num, false);
pwm_set_clkdiv_int_frac(slice_num, 61, (3 << 4) / 100); // 125 MHz / 61.03 / 512 = 4 kHz
pwm_set_enabled(slice_num, true);
sleep_ms(200);
pwm_set_enabled(slice_num, false);
gpio_init(BUZZER_PIN);
gpio_put(BUZZER_PIN, false);
gpio_set_dir(BUZZER_PIN, true);
sleep_ms(2400);
pwm_set_enabled(slice_num, false);
gpio_init(BUZZER_PIN);
gpio_put(OUTPUT_ENABLE_PIN, false);
sseg_init();
configure_ms_trig_alarm();
arm_ms_trig_alarm();
uint32_t ms_counter = 0;
while (true) {
sseg_loop();
if(m_ms_triggered) {
m_ms_triggered = false;
ms_counter++;
sseg_set_char(2, '0' + (ms_counter / 100) % 10, false);
sseg_set_char(1, '0' + (ms_counter / 1000) % 10, true);
sseg_set_char(0, '0' + (ms_counter / 10000) % 10, false);
}
}
return 0;
}

22
src/pinout.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef PINOUT_H
#define PINOUT_H
#define OUTPUT_ENABLE_PIN 22
#define SSEG_MUX_1_PIN 8
#define SSEG_MUX_2_PIN 9
#define SSEG_MUX_3_PIN 10
#define SSEG_A_PIN 4
#define SSEG_B_PIN 5
#define SSEG_C_PIN 6
#define SSEG_D_PIN 3
#define SSEG_E_PIN 2
#define SSEG_F_PIN 1
#define SSEG_G_PIN 0
#define SSEG_DP_PIN 7
#define BUZZER_PIN 11
#endif // PINOUT_H

145
src/sseg.c Normal file
View file

@ -0,0 +1,145 @@
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "sseg.pio.h"
#include "sseg.h"
#include "pinout.h"
// Segment Layout:
//
// .--A--.
// | |
// F B
// | |
// +--G--+
// | |
// E C
// | |
// .--D--. .DP.
#define SSEG_START_PIN 0
static volatile uint32_t m_dma_buffer[4] __attribute__((aligned(4*sizeof(uint32_t *)))) = {
0x0000013D, // segment 0 = 'Z' = '2'
0x00000277, // segment 1 = A
0x00000476, // segment 2 = M
0x00000000, // dummy entry
};
static PIO m_pio;
static uint m_sm;
static int m_dma_chan;
static uint8_t ascii_to_sseg(char c)
{
switch(c) {
case '0':
return 0x7E;
case '1':
return 0x60;
case 'Z':
case '2':
return 0x3D;
case '3':
return 0x79;
case '4':
return 0x63;
case '5':
return 0x5B;
case '6':
return 0x5F;
case '7':
return 0x70;
case '8':
return 0x7F;
case '9':
return 0x7B;
case 'M':
return 0x76;
case 'A':
return 0x77;
default: // '-'
return 0x01;
}
}
void sseg_init(void)
{
// set up PIO for 7-segment display
m_pio = pio0;
uint offset = pio_add_program(m_pio, &sseg_program);
m_sm = pio_claim_unused_sm(m_pio, true);
pio_sm_config c = sseg_program_get_default_config(offset);
sm_config_set_clkdiv_int_frac(&c, 2000, 0);
sm_config_set_out_pins(&c, SSEG_START_PIN, 11);
sm_config_set_set_pins(&c, SSEG_START_PIN+8, 3);
pio_gpio_init(m_pio, SSEG_A_PIN);
pio_gpio_init(m_pio, SSEG_B_PIN);
pio_gpio_init(m_pio, SSEG_C_PIN);
pio_gpio_init(m_pio, SSEG_D_PIN);
pio_gpio_init(m_pio, SSEG_E_PIN);
pio_gpio_init(m_pio, SSEG_F_PIN);
pio_gpio_init(m_pio, SSEG_G_PIN);
pio_gpio_init(m_pio, SSEG_DP_PIN);
pio_gpio_init(m_pio, SSEG_MUX_1_PIN);
pio_gpio_init(m_pio, SSEG_MUX_2_PIN);
pio_gpio_init(m_pio, SSEG_MUX_3_PIN);
pio_sm_set_consecutive_pindirs(m_pio, m_sm, SSEG_START_PIN, 11, true);
pio_sm_init(m_pio, m_sm, offset, &c);
m_dma_chan = dma_claim_unused_channel(true);
dma_channel_config dma_config = dma_channel_get_default_config(m_dma_chan);
channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32);
channel_config_set_read_increment(&dma_config, true);
channel_config_set_write_increment(&dma_config, false);
channel_config_set_ring(&dma_config, false, 4); // read ringbuffer with 2^4 = 16 bytes = 4 entries
channel_config_set_dreq(&dma_config, DREQ_PIO0_TX0);
dma_channel_configure(
m_dma_chan,
&dma_config,
&pio0_hw->txf[0], // Write address (only need to set this once)
m_dma_buffer, // Read address
1<<14, // Write the buffer many times, then halt and interrupt
true // Start right now
);
pio_sm_set_enabled(m_pio, m_sm, true);
}
void sseg_loop(void)
{
// restart the DMA channel if it has completed the transfer
if((dma_hw->intr & (1u << m_dma_chan)) != 0) {
dma_hw->ints0 = 1u << m_dma_chan;
dma_channel_start(m_dma_chan);
}
}
void sseg_set_char(uint8_t seg, char c, bool with_dot)
{
uint dot_bit = with_dot ? 0x80 : 0x00;
m_dma_buffer[seg] = (m_dma_buffer[seg] & 0xFFFFFF00) | dot_bit | ascii_to_sseg(c);
}

12
src/sseg.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef SSEG_H
#define SSEG_H
#include <stdint.h>
#include <stdbool.h>
void sseg_init(void);
void sseg_loop(void);
void sseg_set_char(uint8_t seg, char c, bool with_dot);
#endif // SSEG_H

10
src/sseg.pio Normal file
View file

@ -0,0 +1,10 @@
.program sseg
; Repeatedly get one word of data from the TX FIFO, stalling when the FIFO is
; empty. Write the least significant bit to the OUT pin group.
loop:
set pins, 0x00 ; set MUX pins to 0
pull
out pins, 11 [31] ; write the data from the FIFO to the outputs and wait some time
jmp loop [31] ; repeat and wait some extra time