Various updates

master
Tony 2024-10-15 11:19:21 +01:00
rodzic 0226b83b60
commit 9ee02ab3c9
15 zmienionych plików z 1258 dodań i 778 usunięć

Wyświetl plik

@ -1,24 +1,81 @@
add_executable(FunctionGenerator)
pico_generate_pio_header(FunctionGenerator ${CMAKE_CURRENT_LIST_DIR}/blink.pio)
pico_generate_pio_header(FunctionGenerator ${CMAKE_CURRENT_LIST_DIR}/DAC.pio)
# Generated Cmake Pico project file
# pull in common dependencies
target_sources(FunctionGenerator PRIVATE FunctionGenerator.cpp)
cmake_minimum_required(VERSION 3.13)
# pull in common dependencies and additional spi hardware support
target_link_libraries(FunctionGenerator PRIVATE
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
# == DO NEVER EDIT THE NEXT LINES for Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.0.0)
set(toolchainVersion 13_2_Rel1)
set(picotoolVersion 2.0.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(Function_Generator C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(Function_Generator FunctionGenerator.cpp )
pico_set_program_name(Function_Generator "Function_Generator")
pico_set_program_version(Function_Generator "0.1")
# Generate PIO header
# NOTE: Ninja doesn't always work with relative paths, so best to replace with an absolute...
# pico_generate_pio_header(Function_Generator ${CMAKE_CURRENT_LIST_DIR}/DAC.pio)
pico_generate_pio_header(Function_Generator "C:/Github/Sandpit/RP2040-code/Function Generator/DAC.pio")
# pull in common dependencies...
target_sources(Function_Generator PRIVATE
DAC_Class.cpp
FunctionGenerator.cpp
ClockModule.cpp
Demo.cpp
SPI_Utils.cpp
)
# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(Function_Generator 0)
pico_enable_stdio_usb(Function_Generator 1)
# Add the standard library to the build
target_link_libraries(Function_Generator
pico_stdlib
hardware_pio
hardware_dma
hardware_spi)
# Add the standard include files to the build
target_include_directories(Function_Generator PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
# Add any user requested libraries
target_link_libraries(Function_Generator
hardware_spi
hardware_dma
hardware_pio
)
# enable usb output, disable uart output
pico_enable_stdio_usb(FunctionGenerator 1)
pico_enable_stdio_uart(FunctionGenerator 0)
# create map/bin/hex file etc.
pico_add_extra_outputs(FunctionGenerator)
# add url via pico_set_program_url
example_auto_set_url(FunctionGenerator)
pico_add_extra_outputs(Function_Generator)

Wyświetl plik

@ -0,0 +1,159 @@
#include "ClockModule.h"
// Data for the clock face has been generated externally using an Excel spreadsheet...
uint8_t FaceX[] = {
0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfd,0xfd,0xfd,0xfc,0xfc,0xfb,0xfb,0xfa,0xfa,0xf9,0xf8,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf3,0xf2,0xf1,0xf0,0xef,0xe9,0xea,0xeb,0xec,0xed,0xed,0xec,0xeb,0xea,0xe9,0xe7,0xe6,0xe5,0xe3,0xe2,0xe1,0xdf,0xde,0xdc,0xdb,0xd9,0xd8,0xd6,0xd4,0xd3,0xd1,0xcf,0xcd,0xcc,0xca,0xc8,0xc6,0xc4,0xc3,0xc1,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xad,0xab,0xa9,0xa6,0xa4,0xa2,0xa0,0x9e,0x9c,0x9a,0x97,0x95,0x93,0x91,0x8f,0x8c,0x8a,0x88,0x86,0x83,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7d,0x7b,0x78,0x76,0x74,0x72,0x6f,0x6d,0x6b,0x69,0x67,0x64,0x62,0x60,0x5e,0x5c,0x5a,0x58,0x55,0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47,0x45,0x43,0x41,0x42,0x41,0x41,0x40,0x40,0x3f,0x3d,0x3b,0x3a,0x38,0x36,0x34,0x32,0x31,0x2f,0x2d,0x2b,0x2a,0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1d,0x1c,0x1b,0x19,0x18,0x17,0x15,0x14,0x13,0x12,0x15,0x14,0x13,0x12,0x11,0x11,0x0f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0a,0x09,0x08,0x07,0x06,0x06,0x05,0x04,0x04,0x03,0x03,0x02,
0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x15,0x14,0x13,0x12,0x11,0x11,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1b,0x1c,0x1d,0x1f,0x20,0x22,0x23,0x25,0x26,0x28,0x2a,0x2b,0x2d,0x2f,0x31,0x32,0x34,0x36,0x38,0x3a,0x3b,0x3d,0x42,0x41,0x41,0x40,0x40,0x3f,0x41,0x43,0x45,0x47,0x49,0x4b,0x4d,0x4f,0x51,0x53,0x55,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,0x67,0x69,0x6b,0x6d,0x6f,0x72,0x74,0x76,0x78,0x7b,0x7d,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x81,0x83,0x86,0x88,0x8a,0x8c,0x8f,0x91,0x93,0x95,0x97,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbb,0xbd,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xc1,0xc3,0xc4,0xc6,0xc8,0xca,0xcc,0xcd,0xcf,0xd1,0xd3,0xd4,0xd6,0xd8,0xd9,0xdb,0xdc,0xde,0xdf,0xe1,0xe2,0xe3,0xe5,0xe6,0xe7,0xe9,0xea,0xeb,0xec,0xe9,0xea,0xeb,0xec,0xed,0xed,0xef,0xf0,0xf1,0xf2,0xf3,0xf3,0xf4,0xf5,
0xf6,0xf7,0xf8,0xf8,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f,0x0e,0x0d,0x0c,0x0c,0x0c,0x0c,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x0e,0x0d,0x0c,0x0c,0x0c,0x0c,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x1b,0x52,0x51,0x50,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x46,0x46,0x46,0x46,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,
0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x50,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x82,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xe0,0xe1,0xe2,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd7,0xd7,0xd7,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,0xe1,0xe2,0xe3,0xe3,0xe3,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd7,0xd7,0xd7,0xd8,0xd9,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
0xeb,0xec,0xed,0xee,0xef,0xf0,0xf0,0xf0,0xf0,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x75,0x75,0x75,0x75,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,} ;
uint8_t FaceY[] = {
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7d,0x7b,0x78,0x76,0x74,0x72,0x6f,0x6d,0x6b,0x69,0x67,0x64,0x62,0x60,0x5e,0x5c,0x5a,0x58,0x55,0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47,0x45,0x43,0x41,0x42,0x41,0x41,0x40,0x40,0x3f,0x3d,0x3b,0x3a,0x38,0x36,0x34,0x32,0x31,0x2f,0x2d,0x2b,0x2a,0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1d,0x1c,0x1b,0x19,0x18,0x17,0x15,0x14,0x13,0x12,0x15,0x14,0x13,0x12,0x11,0x11,0x0f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0a,0x09,0x08,0x07,0x06,0x06,0x05,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x15,0x14,0x13,0x12,0x11,0x11,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1b,0x1c,0x1d,0x1f,0x20,0x22,0x23,0x25,0x26,0x28,0x2a,0x2b,0x2d,0x2f,0x31,0x32,0x34,0x36,0x38,0x3a,0x3b,0x3d,0x42,0x41,0x41,0x40,0x40,0x3f,0x41,0x43,0x45,0x47,0x49,0x4b,0x4d,0x4f,0x51,0x53,0x55,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,
0x67,0x69,0x6b,0x6d,0x6f,0x72,0x74,0x76,0x78,0x7b,0x7d,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x81,0x83,0x86,0x88,0x8a,0x8c,0x8f,0x91,0x93,0x95,0x97,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbb,0xbd,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xc1,0xc3,0xc4,0xc6,0xc8,0xca,0xcc,0xcd,0xcf,0xd1,0xd3,0xd4,0xd6,0xd8,0xd9,0xdb,0xdc,0xde,0xdf,0xe1,0xe2,0xe3,0xe5,0xe6,0xe7,0xe9,0xea,0xeb,0xec,0xe9,0xea,0xeb,0xec,0xed,0xed,0xef,0xf0,0xf1,0xf2,0xf3,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf8,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfd,0xfd,0xfd,0xfc,0xfc,0xfb,0xfb,0xfa,0xfa,0xf9,0xf8,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf3,0xf2,0xf1,0xf0,0xef,0xe9,0xea,0xeb,0xec,0xed,0xed,0xec,0xeb,0xea,0xe9,0xe7,0xe6,0xe5,0xe3,0xe2,0xe1,0xdf,0xde,0xdc,0xdb,0xd9,0xd8,0xd6,0xd4,0xd3,0xd1,0xcf,0xcd,0xcc,0xca,0xc8,0xc6,0xc4,0xc3,0xc1,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,
0xad,0xab,0xa9,0xa6,0xa4,0xa2,0xa0,0x9e,0x9c,0x9a,0x97,0x95,0x93,0x91,0x8f,0x8c,0x8a,0x88,0x86,0x83,0x81,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x3e,0x3d,0x3c,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x78,0x77,0x76,0x75,0x75,0x75,0x75,0x75,0x75,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x88,0x87,0x86,0xb3,0xb2,0xb1,0xb0,0xaf,0xae,0xad,0xac,0xab,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xe2,0xe3,0xe4,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xda,
0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xe0,0xdf,0xde,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xde,0xdf,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf0,0xf1,0xf1,0xf1,0xf1,0xf1,0xf1,0xf1,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe7,0xe8,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb2,0xb1,0xb0,0xaf,0xae,0xad,0xac,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0x87,0x88,0x89,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x76,0x76,0x76,
0x76,0x76,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7f,0x7e,0x7d,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x4d,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,0x3f,0x3e,0x3d,0x3c,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,} ;
// (Number of pixels: 1000)
// Storage for clock hands co-ordinates...
uint8_t HandsX[192] = {} ; // Each hand requires 64 bytes - 3x64=192
uint8_t HandsY[192] = {} ;
void HlpTxt(char c); // Forward deffinition
void ClockModule ( DAC DACobj[] ) {
char c;
bool InvX=false, InvY=false ; // Clock display mode flags to allow inverted output
uint32_t GPIO_mask_X=0x0ff<<DAC_A_Start ; // Mask defining which GPIO's connect to the R2R networks
uint32_t GPIO_mask_Y=0x0ff<<DAC_B_Start ;
printf("\nClock>") ; // Clock module cursor
// Disable the Ctrl channels...
hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
// wait for Busy flag to clear...
// Abort the data channels...
dma_channel_abort(DACobj[_DAC_A].data_chan);
dma_channel_abort(DACobj[_DAC_B].data_chan);
// Re-enable the Ctrl channels (doesn't restart data transfer)...
hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
pio_sm_set_enabled(_PIOnum_,0,false) ;
pio_sm_set_enabled(_PIOnum_,1,false) ;
// Grab the GPIO from the State machines...
for (uint i=0; i<8; i++) {
gpio_init(DAC_A_Start + i);
gpio_init(DAC_B_Start + i);
gpio_set_dir(DAC_A_Start + i, GPIO_OUT);
gpio_set_dir(DAC_B_Start + i, GPIO_OUT);
}
_Result.Txt[0] = '\0' ; // String also used as a flag, so needs to be cleared
// while (_Result.Txt[0] == '\0') { // exit on keypress
while (true) { // exit on keypress
float Radians ;
int outX, outY ;
// Draw the clock face...
for (int i=0; i<sizeof(FaceX); i++) {
outX=FaceX[i]*DACobj[_DAC_A].Level/100 ; // Scale output to match state machine settings
outY=FaceY[i]*DACobj[_DAC_B].Level/100 ;
if (!InvX) outX=255-outX ; // Invert output if selected
if (!InvY) outY=255-outY ;
outX=outX<<DAC_A_Start ; // Shift data bits to match R2R GPIO connections
outY=outY<<DAC_B_Start ;
gpio_put_masked(GPIO_mask_X,outX) ;
gpio_put_masked(GPIO_mask_Y,outY) ;
sleep_us(2) ; // Pause for on-screen persistance
}
// Draw the clock hands...
for (int i=0; i<192; i++) { // 3 hands @ 64 pixels each = 192
outX=HandsX[i]*DACobj[_DAC_A].Level/100 ; // Scale output to match state machine settings
outY=HandsY[i]*DACobj[_DAC_B].Level/100 ;
if (!InvX) outX=255-outX ; // Invert output if selected
if (!InvY) outY=255-outY ;
outX=outX<<DAC_A_Start ; // Shift data bits to match R2R GPIO connections
outY=outY<<DAC_B_Start ;
gpio_put_masked(GPIO_mask_X,outX) ;
gpio_put_masked(GPIO_mask_Y,outY) ;
sleep_us(2) ; // Pause for on-screen persistance
}
c = getchar_timeout_us(0); // Non-blocking char input
if (c!=EOF) { // c=EOF if no input
if ((c=='x') or (c=='X')) {
InvX = !InvX ;
if (InvX) printf("%c%sX axis inverted.",c,MarginVW) ; // Print current status
else printf("%c%sX axis NOT inverted.",c,MarginVW) ;
printf("\nClock>") ; // Cursor
}
else if ((c=='y') or (c=='Y')) {
InvY = !InvY ;
if (InvY) printf("%c%sY axis inverted.",c,MarginVW) ; // Print current status
else printf("%c%sY axis NOT inverted.",c,MarginVW) ;
printf("\nClock>") ; // Cursor
}
else if ((c=='S') or (c=='s')) { // Time set
printf("%c%sSet time (format HH:MM:SS)...",c,MarginVW) ;
getLine() ; // Get the console input
Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0 ; // Reset all command line parameters
int i=0, ParmCnt=0 ; // Reset all command line counters
while (i<strlen(inStr) ) {
if ((inStr[i]==':')||(inStr[i]==',')) { // Next parameter
ParmCnt++ ; }
else if (isdigit(inStr[i])) {
Parm[ParmCnt] *= 10; // Next digit. Bump the existing decimal digits
Parm[ParmCnt] += inStr[i] - '0'; } // Convert character to integer and add
i++ ; // Next character
}
inStr[0]='\0' ; // Reset input buffer
Hours=Parm[0]%24 ; Mins=Parm[1]%60 ; Secs=Parm[2]%60 ; // Set the time from parameters
LEDctr=0 ; // Force update and do it on next tick
printf("\n%s Clock set to %02d:%02d:%02d\nClock>",MarginVW,Hours,Mins,Secs) ;
}
else if ((c=='h') or (c=='H') or (c=='?')) HlpTxt(c);
else if ((c=='q') or (c=='Q')) {
for (uint i=0; i<8; i++) { // Hand the GPIO's back to the State machines
pio_gpio_init(_PIOnum_, DAC_A_Start + i);
pio_gpio_init(_PIOnum_, DAC_B_Start + i);
}
// Reset the data transfer DMA's to the start of the data Bitmap...
dma_channel_set_read_addr(DACobj[_DAC_A].data_chan, &DACobj[_DAC_A].DAC_data[0], false);
dma_channel_set_read_addr(DACobj[_DAC_B].data_chan, &DACobj[_DAC_B].DAC_data[0], false);
pio_sm_set_enabled(_PIOnum_,0,true) ; // Re-enable State machine 0
pio_sm_set_enabled(_PIOnum_,1,true) ; // Re-enable State machine 1
dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels
sprintf(_Result.Txt,"%c%sQuit clock mode\n",c,MarginVW);// Prevents error message on return
return; // exit
}
}
}
}
void HlpTxt(char c) {
// Print Help text aligned to current margin settings...
// Note: Following string requires '%%%%' to print '%'
// HelpText string is copied to _Result.Txt using sprintf - this reduces '%%%%' to '%%'
// _Result.Txt is sent to terminal using printf - this reduces '%%' to '%'
strcpy(MarginVW,Margin); // Re-initialise Variable Width margin...
tmp = strlen(inStr) ; // Get number of command line characters
if (tmp != 0) tmp ++; // Bump to allow for cursor.
// Note: If called at Start-up there will be no input characters
MarginVW[MWidth - tmp] = '\0' ; // Calculate padding required for command characters and cursor
sprintf(_Result.Txt, "%c%sHelp...\n"
"%s%sh/H/? - Help\n"
"%s%sX - Invert X-axis\n"
"%s%sY - Invert Y-axis\n"
"%s%sS - Set time (format HH:MM:SS)\n"
"%s%sQ - Quit (Return to Function Generator)\n"
"\nClock>", // Cursor
c, MarginVW, Margin, MarginVW, Margin, MarginVW, Margin,
MarginVW, Margin, MarginVW, Margin, MarginVW ) ;
printf(_Result.Txt) ; // Update terminal
inStr[0] = '\0' ; // Reset input string
*_Result.Txt = '\0' ;
}

Wyświetl plik

@ -0,0 +1,12 @@
#pragma once
#include "DAC_Class.h"
#include "pico/stdio.h"
#include "pico/time.h"
extern uint8_t FaceX[], FaceY[];
extern uint8_t HandsX[], HandsY[];
extern int tmp, LEDctr, Parm[], Hours, Mins, Secs;
extern char MarginVW[], Margin[], inStr[];
extern void getLine();
extern struct repeating_timer timer;

Wyświetl plik

@ -25,7 +25,7 @@ loop01: ; Loop will run 2
// Note: 1) No divider is specified for the SM, so it will default to the same speed as the CPU.
// 2) Hard coded to use 8 bit DAC hardware
void pio_DAC_program_init(PIO pio, uint sm, uint offset, uint start_pin) {
inline void pio_DAC_program_init(PIO pio, uint sm, uint offset, uint start_pin) {
for (uint i=start_pin; i<(start_pin+8); i++) { pio_gpio_init(pio, i); } // Allow PIO to control GPIO pins as outputs
pio_sm_set_consecutive_pindirs(pio, sm, start_pin, 8, true); // Set the pin direction to output (in PIO)
pio_sm_config c = pio_DAC_program_get_default_config(offset); // Define PIO Configuration structure

Wyświetl plik

@ -0,0 +1,318 @@
// Each DAC channel consists of...
// BitMap data => DMA => FIFO => State Machine => GPIO pins => R-2-R module
// Note: The PIO clock dividers are 16-bit integer, 8-bit fractional, with first-order delta-sigma for the fractional divider.
// This means the clock divisor can vary between 1 and 65536, in increments of 1/256.
// If DAC_div exceeds 2^16 (65,536), the registers will wrap around, and the State Machine clock will be incorrect.
// For frequencies below 34Hz, an additional 63 op-code delay is inserted into the State Machine assembler code. This slows
// down the State Machine operation by a factor of 64, keeping the value of DAC_div within range.
#include "DAC_Class.h"
#include "ClockModule.h"
DAC::DAC(char _name, PIO _pio, uint8_t _GPIO) {
// DAC Constructor
// Parameters...
// _name = Name of this DAC channel instance
// _pio = Required PIO channel
// _GPIO = Port connecting to the MSB of the R-2-R resistor network.
pio = _pio;
PIOnum = pio_get_index(pio) ; // Printer friendly value
GPIO = _GPIO ; // Initialse class value
Funct = _Sine_, Freq = 100, Level = 50 ; // Start-up default values...
Range = 1, Harm = 0, DutyC = 50, RiseT = 50, name = _name ;
name == 'A' ? Phase=0 : Phase=180 ; // Set Phase difference between channels
int _offset;
StateMachine = pio_claim_unused_sm(_pio, true); // Find a free state machine on the specified PIO - error if there are none.
ctrl_chan = dma_claim_unused_channel(true); // Find 2 x free DMA channels for the DAC (12 available)
data_chan = dma_claim_unused_channel(true);
// Configure the state machine to run the DAC program...
_offset = pio_add_program(_pio, &pio_DAC_program); // Use helper function included in the .pio file.
SM_WrapBot = _offset;
pio_DAC_program_init(_pio, StateMachine, _offset, _GPIO);
// Setup the DAC control channel...
// The control channel transfers two words into the data channel's control registers, then halts. The write address wraps on a two-word
// (eight-byte) boundary, so that the control channel writes the same two registers when it is next triggered.
dma_channel_config fc = dma_channel_get_default_config(ctrl_chan); // default configs
channel_config_set_transfer_data_size(&fc, DMA_SIZE_32); // 32-bit txfers
channel_config_set_read_increment(&fc, false); // no read incrementing
channel_config_set_write_increment(&fc, false); // no write incrementing
// !!! THIS RESTRICTS SPEED TO ~730KHz !!!
// fc.ctrl |= DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS; // Set high priority
dma_channel_configure(
ctrl_chan,
&fc,
&dma_hw->ch[data_chan].al1_transfer_count_trig, // txfer to transfer count trigger
&transfer_count,
1,
false
);
// Setup the DAC data channel...
// 32 bit transfers. Read address increments after each transfer.
fc = dma_channel_get_default_config(data_chan);
channel_config_set_transfer_data_size(&fc, DMA_SIZE_32); // 32-bit txfers
channel_config_set_read_increment(&fc, true); // increment the read adddress
channel_config_set_write_increment(&fc, false); // don't increment write address
channel_config_set_dreq(&fc, pio_get_dreq(_pio, StateMachine, true)); // Transfer when PIO SM TX FIFO has space
channel_config_set_chain_to(&fc, ctrl_chan); // chain to the controller DMA channel
channel_config_set_ring(&fc, false, 9); // 8 bit DAC 1<<9 byte boundary on read ptr. This is why we needed alignment!
// !!! THIS RESTRICTS SPEED TO ~730KHz !!!
// fc.ctrl |= DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS; // Set high priority
dma_channel_configure(
data_chan, // Channel to be configured
&fc, // The configuration we just created
&_pio->txf[StateMachine], // Write to FIFO
DAC_data, // The initial read address (AT NATURAL ALIGNMENT POINT)
BitMapSize, // Number of transfers; in this case each is 2 byte.
false // Don't start immediately. All 4 control channels need to start simultaneously
// to ensure the correct phase shift is applied.
);
DAC_channel_mask += (1u << ctrl_chan) ; // Save details of DMA control channel to global variable. This facilitates
// atomic restarts of both channels, and ensures phase lock between channels.
DataCalc() ; // Populate bitmap data.
DACspeed(Freq * Range) ; // Initialise State Machine clock speed.
};
char* DAC::StatusString() {
// Report the status line for the current DAC object.
char TmpStr[4];
if (Range == 1) strcpy(TmpStr," Hz") ; // Assign multiplier suffix
if (Range == 1000) strcpy(TmpStr,"KHz") ;
if (Range == 1000000) strcpy(TmpStr,"MHz") ;
switch ( Funct ) { // Calculate status sting...
case _Sine_:
sprintf(RetStr,"Channel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Sine Harmonic:%d\n",name, Freq, TmpStr, Phase, Level, Harm) ;
return RetStr;
case _Triangle_:
if ((RiseT == 0) || (RiseT == 100)) {
sprintf(RetStr,"Channel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Sawtooth\n",name, Freq, TmpStr, Phase, Level) ;
} else {
sprintf(RetStr,"Channel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Triangle Rise time:%d%%\n",name, Freq, TmpStr, Phase, Level, RiseT) ;
}
return RetStr;
case _Square_:
sprintf(RetStr,"Channel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Square Duty cycle:%d%%\n",name, Freq, TmpStr, Phase, Level, DutyC) ;
return RetStr;
default:
return 0; // Program execution should never get here
}
}
int DAC::Set(int _type, int _val) {
// Multi-purpose routine to set various DAC operating values.
// Parameters...
// _type = operating value to be set (frequency, phase, level, sine, square, triangle, time)
// _val = value for the designated parameter
_Result.Val = _val; // Save for SPI display
switch (_type) {
case _Freq_:
Freq = _val ; // Frequency (numeric)
DACspeed(Freq * Range) ; // Update State machine run speed
break ;
case _Phase_:
Phase = _val ; // Phase shift (0->355 degrees)
DataCalc() ; // Recalc Bitmap and apply new phase value
break ;
case _Level_:
if (_val > 100) _val = 100 ; // Limit max val to 100%
Level = _val ;
MCP41020_Write(SelectedChan, Level) ; // Control byte for the MCP42010 just happens to be the same value as the SelectedChan variable
StatusString() ; // Update the terminal session
break ;
case _Sine_:
Funct = _Sine_ ;
Harm = _val ; // Optional command line parameter (default to zero if not provided)
DataCalc() ;
break ;
case _Square_:
Funct = _Square_ ;
DutyC = _val ; // Optional command line parameter (default to 50% if not provided)
DataCalc() ;
break ;
case _Triangle_:
Funct = _Triangle_ ;
RiseT = _val ; // Optional command line parameter (default to 50% if not provided)
DataCalc() ;
break ;
}
strcat (_Result.Txt,StatusString());
return 0;
}
int DAC::Bump(int _type, int _dirn) {
// Multi-purpose routine to bump various DAC operating values.
// Parameters...
// _type = operating value to be bumped (frequency, phase, level, sine, square, triangle, time)
// _dirn = bump direction for the designated parameter (up, down)
if (_type == _Freq_) {
if ((Freq*Range==0) && (_dirn==_Down)) { // Attempt to bump below lower limit
sprintf(RetStr,"Channel %c: Error - Minimum Frequency\n",name);
strcat(_Result.Txt, RetStr);
return 0;
}
// // TBD - remove hardcoded Max frequency
else if ((Freq*Range==1000000) && (_dirn==_Up)) { // Attempt to bump above upper limit
sprintf(RetStr,"Channel %c: Error - Maximum Frequency\n",name);
strcat(_Result.Txt, RetStr);
return 0;
} else { // Not at max or min value...
Freq += _dirn ; // ... bump
if ((Freq == 1000) && (_dirn == _Up)) { // Range transition point
Freq = 1 ; // Reset count
if (Range == 1) Range = 1000 ; // either Hz=>KHz
else if (Range == 1000) Range = 1000000 ; // or KHz=>MHz
}
if ((Freq==0) && (Range!=1) && (_dirn==_Down)) { // Range transition point
Freq = 999 ; // Reset count
if (Range == 1000) Range = 1 ; // either KHz=>Hz
else if (Range == 1000000) Range = 1000 ; // or MHz=>KHz
}
_Result.Val = Freq ; // Grab value for SPI display
DACspeed(Freq * Range) ; }
}
if (_type == _Phase_) {
Phase += _dirn ;
if (Phase == 360) Phase = 0 ; // Top Endwrap
if (Phase < 0 ) Phase = 359 ; // Bottom Endwrap
_Result.Val = Phase ; // Grab value for SPI display
DataCalc(); } // Update Bitmap data to include new DAC phase
if (_type == _Level_) {
Level += _dirn ;
if (Level > 100) { Level = 0 ; } // Top endwrap
if (Level < 0 ) { Level = 100 ; } // Bottom endwrap
_Result.Val = Level ; // Grab value for SPI display
MCP41020_Write(SelectedChan, Level) ; // Control byte for the MCP42010 just happens to be the same value as the SelectedChan variable
StatusString () ; } // Update the terminal session
if (_type == _Square_) {
DutyC += _dirn ;
if (DutyC > 100) { DutyC = 0 ; } // Top endwrap
if (DutyC < 0 ) { DutyC = 100 ; } // Bottom endwrap
_Result.Val = DutyC ; // Grab value for SPI display
DataCalc(); } // Update Bitmap with new Duty Cycle value
if (_type == _Triangle_) {
RiseT += _dirn ;
if (RiseT > 100) { RiseT = 0 ; } // Top endwrap
if (RiseT < 0 ) { RiseT = 100 ; } // Bottom endwrap
_Result.Val = RiseT ; // Grab value for SPI display
DataCalc(); } // Update Bitmap with new Duty Cycle value
if (_type == _Sine_) {
Harm += _dirn ;
if (Harm > 10) { Harm = 0 ; } // Top endwrap
if (Harm < 0 ) { Harm = 9 ; } // Bottom endwrap
_Result.Val = Harm ; // Grab value for SPI display
DataCalc(); } // Update Bitmap with new Sine harmonic value
strcat (_Result.Txt,StatusString());
return 0;
}
void DAC::DACspeed(int _frequency) {
// If DAC_div exceeds 2^16 (65,536), the registers wrap around, and the State Machine clock will be incorrect.
// A slow version of the DAC State Machine is used for frequencies below 17Hz, allowing the value of DAC_div to
// be kept within the working range.
float DAC_freq = _frequency * BitMapSize; // Target frequency...
DAC_div = 2 * (float)clock_get_hz(clk_sys) / DAC_freq; // ...calculate the PIO clock divider required for the given Target frequency
float Fout = 2 * (float)clock_get_hz(clk_sys) / (BitMapSize * DAC_div); // Actual output frequency
if (_frequency >= 34) { // Fast DAC ( Frequency range from 34Hz to 999Khz )
SM_WrapTop = SM_WrapBot ; // SM program memory = 1 op-code
pio_sm_set_wrap (pio, StateMachine, SM_WrapBot, SM_WrapTop) ; // Fast loop (1 clock cycle)
// If the previous frequency was < 33Hz, we will have just shrunk the assembler from 4 op-codes down to 1.
// This leaves the State Machine program counter pointing outside of the new WRAP statement, which crashes the SM.
// To avoid this, we need to also reset the State Machine program counter...
pio->sm[StateMachine].instr = SM_WrapBot ; // Reset State Machine PC to start of code
pio_sm_set_clkdiv(pio, StateMachine, DAC_div); // Set the State Machine clock
} else { // Slow DAC ( 1Hz=>33Hz )
DAC_div = DAC_div / 64; // Adjust DAC_div to keep within useable range
DAC_freq = DAC_freq * 64;
SM_WrapTop = SM_WrapBot + 3 ; // SM program memory = 4 op-codes
pio_sm_set_wrap (pio, StateMachine, SM_WrapBot, SM_WrapTop) ; // slow loop (64 clock cycles)
// If the previous frequency was >= 34Hz, we will have just expanded the assembler code from 1 op-code up to 4.
// The State Machine program counter will still be pointing to an op-code within the new WRAP statement, so will not crash.
pio_sm_set_clkdiv(pio, StateMachine, DAC_div); // Set the State Machine clock speed
}
}
void DAC::DataCalc() {
// Calculate the bitmap for the various waveform outputs...
int i, j, v_offset = BitMapSize/2 - 1; // Shift sine waves up above X axis
int _phase;
float a,b,x1,x2,g1,g2;
// Scale the phase shift to match data size...
_phase = Phase * BitMapSize / 360 ; // Input range: 0 -> 360 (degrees)
// Output range: 0 -> 255 (bytes)
switch (Funct) {
case _Sine_:
Harm = Harm % 11; // Sine harmonics cycles after 10
for (i=0; i<BitMapSize; i++) {
// Add the phase offset and wrap data beyond buffer end back to the buffer start...
j = ( i + _phase ) % BitMapSize; // Horizontal index
a = v_offset * sin((float)_2Pi*i / (float)BitMapSize); // Fundamental frequency...
if (Harm >= 1) { a += v_offset/3 * sin((float)_2Pi*3*i / (float)BitMapSize); } // Add 3rd harmonic
if (Harm >= 2) { a += v_offset/5 * sin((float)_2Pi*5*i / (float)BitMapSize); } // Add 5th harmonic
if (Harm >= 3) { a += v_offset/7 * sin((float)_2Pi*7*i / (float)BitMapSize); } // Add 7th harmonic
if (Harm >= 4) { a += v_offset/9 * sin((float)_2Pi*9*i / (float)BitMapSize); } // Add 9th harmonic
if (Harm >= 5) { a += v_offset/11 * sin((float)_2Pi*11*i / (float)BitMapSize); } // Add 11th harmonic
if (Harm >= 6) { a += v_offset/13 * sin((float)_2Pi*13*i / (float)BitMapSize); } // Add 13th harmonic
if (Harm >= 7) { a += v_offset/15 * sin((float)_2Pi*15*i / (float)BitMapSize); } // Add 15th harmonic
if (Harm >= 8) { a += v_offset/17 * sin((float)_2Pi*17*i / (float)BitMapSize); } // Add 17th harmonic
if (Harm >= 9) { a += v_offset/19 * sin((float)_2Pi*19*i / (float)BitMapSize); } // Add 19th harmonic
if (Harm >= 10) { a += v_offset/20 * sin((float)_2Pi*20*i / (float)BitMapSize); } // Add 21st harmonic
DAC_data[j] = (int)(a)+v_offset; // Sum all harmonics and add vertical offset
}
break;
case _Square_:
b = DutyC * BitMapSize / 100; // Convert % to value
for (i=0; i<BitMapSize; i++) {
j = ( i + _phase ) % BitMapSize; // Horizontal index
if (b <= i) { DAC_data[j] = 0; } // First section low
else { DAC_data[j] = 255; } // Second section high
}
break;
case _Triangle_:
x1 = (RiseT * BitMapSize / 100) -1; // Number of data points to peak
x2 = BitMapSize - x1; // Number of data points after peak
g1 = (BitMapSize - 1) / x1; // Rising gradient (Max val = BitMapSize -1)
g2 = (BitMapSize - 1) / x2; // Falling gradient (Max val = BitMapSize -1)
for (i=0; i<BitMapSize; i++) {
j = ( i + _phase ) % BitMapSize; // Horizontal index
if (i <= x1) { DAC_data[j] = i * g1; } // Rising section of waveform...
if (i > x1) { DAC_data[j] = (BitMapSize - 1) - ((i - x1) * g2); } // Falling section of waveform
}
break ;
}
}
// The following function is not really a member of the DAC class, as it operates across the two DAC objects simultaneously.
// Code is included here for the purposes of clarity.
void PhaseLock( DAC DACobj[2] ) {
// Phase lock the two DAC channels...
// Parameter...
// DACobj[2] = Array containing the two DAC objects.
// Disable the Ctrl channels...
hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
// Abort the data channels...
dma_channel_abort(DACobj[_DAC_A].data_chan);
dma_channel_abort(DACobj[_DAC_B].data_chan);
// Reset the data transfer DMA's to the start of the data Bitmap...
dma_channel_set_read_addr(DACobj[_DAC_A].data_chan, &DACobj[_DAC_A].DAC_data[0], false);
dma_channel_set_read_addr(DACobj[_DAC_B].data_chan, &DACobj[_DAC_B].DAC_data[0], false);
// Re-enable the Ctrl channels (doesn't restart data transfer)...
hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS);
dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels
}

Wyświetl plik

@ -0,0 +1,58 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/dma.h"
#include "DAC.pio.h"
#include "SPI_Utils.h"
#define BitMapSize 256
#define _Up 1
#define _Down -1
#define _Sine_ 0
#define _Square_ 1
#define _Triangle_ 2
//#define _Time_ 3
#define _Freq_ 6
#define _Level_ 7
// Custom data type used to store both text and numeric data results.
// Txt value is the string to be passed to the terminal display.
// Num value is passed to a 3 digit numeric display module using an SPI connection.
// Note: defined outside of class, so can be re-used by other modules.
struct Result {
char Txt[3000];
int Val; };
extern unsigned short DAC_channel_mask;
extern const uint32_t transfer_count;
extern int MarginCount, SelectedChan, Value;
extern Result _Result;
class DAC {
public:
PIO pio;
unsigned short DAC_data[BitMapSize] __attribute__ ((aligned(2048))) ; // Align DAC data (2048d = 0800h)
// uint8_t DAC_data[BitMapSize] __attribute__ ((aligned(2048))) ; // Align DAC data (2048d = 0800h)
int Funct, Range, PIOnum, Level, Freq, Phase, DutyC, Harm, RiseT ;
uint8_t StateMachine, ctrl_chan, data_chan, GPIO, SM_WrapBot, SM_WrapTop ;
float DAC_div ;
char name;
DAC(char _name, PIO _pio, uint8_t _GPIO);
char* StatusString(); // TBD - is this the same as RetStr ???
int Set(int _type, int _val);
int Bump(int _type, int _dirn);
void DACspeed(int _frequency);
void DataCalc();
private:
const float _2Pi = 6.283; // 2*Pi
char RetStr[300];
};
void PhaseLock(DAC DACobj[2]); // Not a member of the class

Wyświetl plik

@ -0,0 +1,115 @@
#include "Demo.h"
bool SweepParm (DAC DACobj[], int _parm, int _start, int _stop, int _speed, int _pause) {
// Returns true when complete, returns false if 'Q' or 'q' pressed.
char c ;
int i=_start;
int step, count ;
if (_start<=_stop) { step=1 ; } // Increment
else { step=-1 ; } // Decrement
while (true) {
count=0 ;
i=i+step ;
if (SelectedChan & 0b01) DACobj[_DAC_A].Set(_parm,i) ; // Set frequency, display status
if (SelectedChan & 0b10) DACobj[_DAC_B].Set(_parm,i) ;
printf(_Result.Txt) ; // Update terminal
_Result.Txt[0] = '\0' ; // Reset the string variable
SPI_Display_Write(i) ;
if ((i==_start)or(i==_stop)) break ; // End of sweep
// Loop to create a short pause between steps of the sweep.
// Note: keyboard input is still scanned providing immediate exit on key press
while (count<_speed) {
c=getchar_timeout_us(0);
count++;
sleep_ms(1) ;
if ((c=='q') or (c=='Q')) {
while (c!=254) {
c=getchar_timeout_us(0) ; // wait for key release
sleep_ms(1) ;
}
return false ;
}
}
} // Falls through here when sweep has completed.
// Second loop creates a longer pause at the end of each sweep.
// Note: keyboard input is still scanned providing immediate exit on key press
count=0 ;
while (count<_pause) {
c=getchar_timeout_us(0);
count++;
sleep_ms(1) ;
if ((c=='q') or (c=='Q')) {
while (c!=254) {
c=getchar_timeout_us(0) ; // wait for key release
sleep_ms(1) ;
}
return false ;
}
}
return true ;
}
void Demo_01 (DAC DACobj[]) {
// Demo 01: Standard waveforms
// Sine with varying harmonics
// Triangle with varying rise times
// Square wave with varying duty cycle
// Frequency and Level of the Demo can be set through the Function Generator interface.
int speed=5 ; // Pause between steps (ms)
int pause=1000 ; // Pause between stages (ms)
int Maxlevel ; // Max level (amplitude) of the DAC channels
// Set Maxlevel to the largest existing DAC level. This allows us to use the Function Generator mode to scale the
// vertical output to fit the oscilloscope screen. IF we re-use this value, we ensure the demo will also fit the oscilloscope
// vertical screen size .
if (DACobj[_DAC_A].Level<DACobj[_DAC_B].Level) Maxlevel=DACobj[_DAC_B].Level ; // Bigger of the two values
else Maxlevel=DACobj[_DAC_A].Level ;
SelectedChan=0b011 ; // Select both channels
SetVal(DACobj,_Level_,0) ; // Set Output level 0
_Result.Txt[0] = '\0' ; // String also used as a flag, so needs to be cleared
while (true) {
SetVal(DACobj,_Triangle_,50) ; // Set Triangle wave, 50% rise time
if (!SweepParm(DACobj,_Level_,0,Maxlevel,speed,pause)) break ; // Sweep up - exit on keypess
if (!SweepParm(DACobj,_Triangle_,50,0,speed,pause)) break ; // Rise time -> 0 - exit on keypress
if (!SweepParm(DACobj,_Triangle_,0,100,speed,pause)) break ; // Rise time -> 100 - exit on keypress
if (!SweepParm(DACobj,_Triangle_,100,50,speed,pause)) break ; // Rise time -> 50 - exit on keypress
if (!SweepParm(DACobj,_Level_,Maxlevel,0,speed,pause)) break ; // Sweep down - exit on keypress
SetVal(DACobj,_Sine_,0) ; // Set Sine wave, no harmonics
if (!SweepParm(DACobj,_Level_,0,Maxlevel,speed,pause)) break ; // Sweep up - exit on keypess
if (!SweepParm(DACobj,_Sine_,0,5,pause,pause)) break ; // Harmonic -> 5 - exit on keypress
if (!SweepParm(DACobj,_Sine_,5,0,pause,pause)) break ; // Harmonic -> 0 - exit on keypress
if (!SweepParm(DACobj,_Level_,Maxlevel,0,speed,pause)) break ; // Sweep down - exit on keypress
SetVal(DACobj,_Square_,50) ; // Set Square wave, 50% duty cycle
if (!SweepParm(DACobj,_Level_,0,Maxlevel,speed,pause)) break ; // Sweep up - exit on keypess
if (!SweepParm(DACobj,_Square_,50,0,speed,pause)) break ; // Duty cycle -> 0 - exit on keypress
if (!SweepParm(DACobj,_Square_,0,100,speed,pause)) break ; // Duty cycle -> 100 - exit on keypress
if (!SweepParm(DACobj,_Square_,100,50,speed,pause)) break ; // Duty cycle -> 50 - exit on keypress
if (!SweepParm(DACobj,_Level_,Maxlevel,0,speed,pause)) break ; // Sweep down - exit on keypress
}
sprintf(_Result.Txt,"%sQuit demo #1\n",MarginVW); // Prevents error message on return
}
void Demo_02 (DAC DACobj[]) {
// Demo 02: Lazy Lissajous
int speed=5 ; // Pause between steps (ms)
int pause=0 ; // Pause between stages (ms)
_Result.Txt[0] = '\0' ; // String also used as a flag, so needs to be cleared
SelectedChan=0b001 ; // Select channel A
SetVal(DACobj,_Freq_,100) ; // Set 100Hz
while (true) {
SetVal(DACobj,_Sine_,0) ; // Set Sine wave, no harmonics
SelectedChan=0b010 ; // Select channel B
SetVal(DACobj,_Freq_,100) ; // Set 100Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
SetVal(DACobj,_Freq_,200) ; // Set 200Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
SetVal(DACobj,_Freq_,300) ; // Set 300Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
SetVal(DACobj,_Freq_,400) ; // Set 400Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
SetVal(DACobj,_Freq_,500) ; // Set 500Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
SetVal(DACobj,_Freq_,600) ; // Set 600Hz
if (!SweepParm(DACobj,_Phase_,0,1400,speed,pause)) break ; // Sweep phase - exit on keypess
}
sprintf(_Result.Txt,"%sQuit demo #2\n",MarginVW); // Prevents error message on return
}

Wyświetl plik

@ -0,0 +1,12 @@
#pragma once
#include "DAC_Class.h"
#include "pico/stdlib.h"
extern int Parm[] ;
extern int SetVal(DAC DACobj[], int _Parm, int _Value) ;
extern char MarginVW[], Margin[], inStr[] ;
bool SweepParm (DAC DACobj[], int _parm, int _start, int _stop, int _speed, int pause) ;
void Demo_01 (DAC DACobj[]) ;
void Demo_02 (DAC DACobj[]) ;

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,77 @@
#pragma once
//#define NewHardware // Comment/Uncomment to select active GPIO ports
#ifdef NewHardware
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Define GPIO connections for Pico (new hardware)...
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Phase lock:
// Read/Write through serial port does not use DMA channels. This removes most DAC channels jitter at
// high frequencies.
// 1 Hz => 900 KHz - Phase sync stable
// 900 KHz => 1 MHz - Phase sync usable - random drifting easily corrected by hitting 'return'
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SPI_PORT spi1 // Using SPI port 1
// ┌──────────┬───────────────┬─────────────┐────────────────┐
// │ PGA2040 │ Connection │ MCP41010 │ Display module │
// ├──────────┼───────────────┼─────────────┤────────────────┤
#define PIN_RX 12 // │ GPIO 12 │ SPI1 RX │ (unused) │ (unused) │
#define Display_CS 26 // │ GPIO 17 │ Chip select │ (unused) │ SS1 (white) │
#define PIN_CLK 10 // │ GPIO 10 │ SPI1 Clock │ SCK (Pin 2) │ SCK (blue) │
#define PIN_TX 11 // │ GPIO 11 │ SPI1 TX │ SI (Pin 3) │ SDI (green) │
#define Level_CS 22 // │ GPIO 22 │ Chip select │ CS (Pin 1) │ (unused) │
// └──────────┴───────────────┴─────────────┘────────────────┘
#define DAC_A_Start 2 // First GPIO port used by DAC A
#define DAC_B_Start 14 // First GPIO port used by DAC B
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#else
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Define GPIO connections for Pico (Module)...
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Phase lock:
// Read/Write through USB serial port requires DMA channel usage. This makes the DAC channels jitter at
// high frequencies and causes phase sync issues.
// 1 Hz => 250 KHz - Phase sync stable
// 250 KHz => 750 KHz - Phase sync usable - some drifting noticable when using USB serial input
// 750 KHz => 1 MHz - Phase sync unusable - random drifting
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Note: 1) The SPI Port only works through specific pins, so these connections are defined first.
// 2) Pin assignments prevent the use of the UART for this configuration.
// SPI Port connections...
#define SPI_PORT spi0 // Using SPI port 0
// ┌──────────┬───────────────┬─────────────┐────────────────┐
// │ PGA2040 │ Connection │ MCP41010 │ Display module │
// ├──────────┼───────────────┼─────────────┤────────────────┤
#define PIN_RX 16 // │ GPIO 16 │ SPI0 RX │ (unused) │ (unused) │
#define Display_CS 17 // │ GPIO 17 │ Chip select │ (unused) │ SS1 (white) │
#define PIN_CLK 18 // │ GPIO 18 │ SPI0 Clock │ SCK (Pin 2) │ SCK (blue) │
#define PIN_TX 19 // │ GPIO 19 │ SPI0 TX │ SI (Pin 3) │ SDI (green) │
#define Level_CS 20 // │ GPIO 20 │ Chip select │ CS (Pin 1) │ (unused) │
// └──────────┴───────────────┴─────────────┘────────────────┘
#define DAC_A_Start 0 // First GPIO port used by DAC A
#define DAC_B_Start 8 // First GPIO port used by DAC B
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif
// Definitions common to either hardware...
#define _DAC_A 0 // DAC channel alias
#define _DAC_B 1 // DAC channel alias
#define _Funct_ 2
#define _Phase_ 3
#define _Duty_ 4
#define _Range_ 5
#define _Harmonic_ 10
#define _PIOnum_ pio0 // Code will work equally well on either pio0 or pio1
#define eof 255 // EOF in stdio.h -is -1, but getchar returns int 255 to avoid blocking
#define CR 13
#define MWidth 12 // Width of terminal command margin (in columns)
// Define clock speed...
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//#define SysClock 125 // System clock for 0.488 MHz DAC output (Pico default)
//#define SysClock 250 // System clock x 2 for 0.977 MHz DAC output
#define SysClock 280 // Overclock for 1.000 MHz DAC output
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Wyświetl plik

@ -0,0 +1,42 @@
#include "SPI_Utils.h"
void SPI_Init(int _clk, int _tx) {
// Initialise the SPI bus hardawre...
spi_init(SPI_PORT, 500000); // Set SPI bus at 0.5MHz...
gpio_set_dir(_clk, GPIO_OUT) ; // Initialise remaining SPI connections...
gpio_set_dir(_tx, GPIO_OUT) ;
gpio_set_function(_clk, GPIO_FUNC_SPI);
gpio_set_function(_tx, GPIO_FUNC_SPI);
}
void cs_select(int _gpio) {
asm volatile("nop \n nop \n nop");
gpio_put(_gpio, 0); // Active low
asm volatile("nop \n nop \n nop");
}
void cs_deselect(int _gpio) {
asm volatile("nop \n nop \n nop");
gpio_put(_gpio, 1); // Inactive high
asm volatile("nop \n nop \n nop");
}
void SPI_Display_Write(int _data) {
uint8_t buff[2];
buff[0] = _data / 256; // MSB data
buff[1] = _data % 256; // LSB data
cs_select(Display_CS);
spi_write_blocking(SPI_PORT, buff, 2);
cs_deselect(Display_CS);
}
void MCP41020_Write (uint8_t _ctrl, uint8_t _data) {
// Parameters: _ctrl - channel to write to ( 0b01=A, 0b10=B, 0b11=Both )
// _data - value to be written ( 0-255 )
uint8_t buff[2];
buff[0] = _ctrl | 0x10 ; // Set control bit to Write data
buff[1] = _data * 2.55 ; // Scale data byte (100%->255)
cs_select(Level_CS) ; // Transmit data to Digi-Pot
spi_write_blocking(SPI_PORT, buff, 2) ;
cs_deselect(Level_CS) ;
}

Wyświetl plik

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "GPIO+Definitions.h"
void SPI_Init(int _clk, int _tx);
void cs_select(int _gpio);
void cs_deselect(int _gpio);
void SPI_Display_Write(int _data);
void MCP41020_Write(uint8_t _ctrl, uint8_t _data);

Wyświetl plik

@ -0,0 +1,84 @@
# 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 ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
endif ()
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
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")
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for 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 ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)
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})