diff --git a/Function Generator/CMakeLists.txt b/Function Generator/CMakeLists.txt index d26e507..c824b52 100644 --- a/Function Generator/CMakeLists.txt +++ b/Function Generator/CMakeLists.txt @@ -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) diff --git a/Function Generator/ClockModule.cpp b/Function Generator/ClockModule.cpp new file mode 100644 index 0000000..21af08a --- /dev/null +++ b/Function Generator/ClockModule.cpp @@ -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<") ; // 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") ; // 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",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' ; +} \ No newline at end of file diff --git a/Function Generator/ClockModule.h b/Function Generator/ClockModule.h new file mode 100644 index 0000000..27c2573 --- /dev/null +++ b/Function Generator/ClockModule.h @@ -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; \ No newline at end of file diff --git a/Function Generator/DAC.pio b/Function Generator/DAC.pio index af3b658..4e62b94 100644 --- a/Function Generator/DAC.pio +++ b/Function Generator/DAC.pio @@ -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 diff --git a/Function Generator/DAC_Class.cpp b/Function Generator/DAC_Class.cpp new file mode 100644 index 0000000..e84c306 --- /dev/null +++ b/Function Generator/DAC_Class.cpp @@ -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= 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 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 +} diff --git a/Function Generator/DAC_Class.h b/Function Generator/DAC_Class.h new file mode 100644 index 0000000..7ef626c --- /dev/null +++ b/Function Generator/DAC_Class.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#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 diff --git a/Function Generator/Demo.cpp b/Function Generator/Demo.cpp new file mode 100644 index 0000000..56a3dd7 --- /dev/null +++ b/Function Generator/Demo.cpp @@ -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 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 +} \ No newline at end of file diff --git a/Function Generator/Demo.h b/Function Generator/Demo.h new file mode 100644 index 0000000..313e6ee --- /dev/null +++ b/Function Generator/Demo.h @@ -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[]) ; \ No newline at end of file diff --git a/Function Generator/FunctionGenerator.cpp b/Function Generator/FunctionGenerator.cpp index 09c063a..453454f 100644 --- a/Function Generator/FunctionGenerator.cpp +++ b/Function Generator/FunctionGenerator.cpp @@ -1,508 +1,151 @@ -// TBD: 1) SPI read connecton +// TBD: 1) SPI read connection // 2) Capacitors on op-amps -// 3) Issue with phase lock - red/writes to serial port affecting phase lock at high frequencies #include #include #include -#include #include "pico/stdlib.h" #include "pico/binary_info.h" #include "hardware/spi.h" -#include "hardware/clocks.h" -#include "hardware/dma.h" -#include "blink.pio.h" -#include "DAC.pio.h" -#include "hardware/gpio.h" // Required for manually toggling GPIO pins (clock) +#include "hardware/gpio.h" -////////////////////////////////////// -// Define GPIO connections for Pico... -////////////////////////////////////// +#include "GPIO+Definitions.h" +#include "DAC_Class.h" +#include "SPI_Utils.h" +#include "ClockModule.h" +#include "Demo.h" -// Note: The SPI Port only works through specific pins, so these connections are defined first. -// 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 0 // DAC channel alias -#define _DAC_B 1 // DAC channel alias -#define _Up 1 -#define _Down -1 -#define _Sine_ 0 // Permited values for variable WaveForm_Type -#define _Square_ 1 -#define _Triangle_ 2 -#define _Time_ 3 -#define _Funct_ 4 -#define _Phase_ 5 -#define _Freq_ 6 -#define _Level_ 7 -#define _Duty_ 8 -#define _Range_ 9 -#define _Harmonic_ 10 -#define eof 255 // EOF in stdio.h -is -1, but getchar returns int 255 to avoid blocking -#define CR 13 -#define BitMapSize 256 // Match X to Y resolution -#define MWidth 12 // Width of terminal command margin (in columns) - -//#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 - -// Data for the clock face is 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) - -// Store clock hands co-ordinates... -uint8_t HandsX[192] = {} ; // Each hand requires 64 bytes - 3x64=192 -uint8_t HandsY[192] = {} ; -int Hours=0, Mins=0, Secs=0, LEDCtr=0, Angle, StartX, StartY, Radius ; +int Hours=0, Mins=0, Secs=0, LEDctr=0, Angle, StartX, StartY, Radius ; float Radians ; - int tmp ; - -char MarginFW[MWidth+1], MarginVW[MWidth+1] ; // Fixed Width & Variable Width strings to create a fixed margin -unsigned short DAC_channel_mask = 0 ; // Binary mask to simultaneously start all DMA channels -const uint32_t transfer_count = BitMapSize ; // Number of DMA transfers per event -const float _2Pi = 6.283; // 2*Pi -int ParmCnt = 0, Parm[4], WaveForm_Type ; // Storage for 4 command line parameters -int SelectedChan, c, i = 0, dirn = 1, result ; -int MarginCount = 0 ; // Manual count of characters written to terminal - required to maintain margins +char Margin[MWidth+1], MarginVW[MWidth+1] ; // Fixed Width & Variable Width strings to create a fixed margin +unsigned short DAC_channel_mask = 0 ; // Binary mask to simultaneously start all DMA channels +const uint32_t transfer_count = BitMapSize ; // Number of DMA transfers per event +int ParmCnt = 0, Parm[4], WaveForm_Type ; // Storage for 4 command line parameters +int SelectedChan, c, i = 0, dirn = 1, Value; float MaxDACfreq ; -char inStr[30], outStr[2500], ResultStr[3000], LastCmd[30] ; // outStr large enough to contain the HelpText string +char inStr[30], outStr[2500], LastCmd[30] ; // outStr large enough to contain the HelpText string +Result _Result; // Custom data structure to store Console and SPI data output +struct repeating_timer timer; -static void MCP41020_Write (uint8_t _ctrl, uint8_t _data) ; // Forward definitions -static void SPI_Display_Write(int _data) ; - -class DAC { -public: - PIO pio; // Class wide var to share value with setter function - unsigned short DAC_data[BitMapSize] __attribute__ ((aligned(2048))) ; // Align DAC data (2048d = 0800h) - int Funct, Range, PIOnum ; - int Level, Freq, Phase, DutyC, Harm, RiseT ; - uint StateMachine, ctrl_chan, data_chan, GPIO, SM_WrapBot, SM_WrapTop ; // Variabes used by the getter function... - char name ; // Name of this instance - float DAC_div ; - - void StatusString () { - // Report the status line for the current DAC object, aligned to current margin settings. - char Str1[4], Str2[200], Margin[40] ; // ! Max line length = 100 chars ! - if (Range == 1) strcpy(Str1," Hz") ; // Asign multiplier suffix - if (Range == 1000) strcpy(Str1,"KHz") ; - if (Range == 1000000) strcpy(Str1,"MHz") ; - tmp = strlen(inStr) ; // Get number of command line characters - // Handle the instances where the length of the command line exceeds the Margin width... - if (MarginCount >= MWidth) { - printf("\n") ; // Start a newline - strcpy(inStr,"") ; // Clear the string - MarginCount = 0 ; // Update the length variable - } - MarginVW[MWidth - MarginCount] = '\0' ; // Calculate padding required for command characters and cursor - if (MarginCount == 0) { strcpy(Margin,MarginFW) ; } // Fixed Width margin if no command characters - else { strcpy(Margin,MarginVW) ; } // Varable Width margin if command characters are present - switch ( Funct ) { // Calculate status sting... - case _Sine_: - sprintf(Str2,"%sChannel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Sine Harmonic:%d\n", Margin, name, Freq, Str1, Phase, Level, Harm) ; - break; - case _Triangle_: - if ((RiseT == 0) || (RiseT == 100)) { - sprintf(Str2,"%sChannel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Sawtooth\n", Margin, name, Freq, Str1, Phase, Level) ; - } else { - sprintf(Str2,"%sChannel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Triangle Rise time:%d%%\n", Margin, name, Freq, Str1, Phase, Level, RiseT) ; - } - break; - case _Square_: - sprintf(Str2,"%sChannel %c: Freq:%3d%s Phase:%03d Level:%03d Wave:Square Duty cycle:%d%%\n", Margin, name, Freq, Str1, Phase, Level, DutyC) ; - break ; - case _Time_: - sprintf(Str2,"%sChannel %c: Freq:%3d%s Phase:%03d Level:%03d Time\n", Margin, name, Freq, Str1, Phase, Level) ; - } - strcat(ResultStr,Str2) ; - inStr[0] = '\0' ; // Reset input string - MarginCount = 0 ; - } - - int Set(int _type, int _val) { - 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 ; - case _Time_: - Funct = _Time_ ; - DataCalc() ; - } - return (_val) ; - } - - int Bump(int _type, int _dirn) { - // _type = Frequency / Phase / Level, Duty, _dirn = Up / Down (_Up = 1, _Down = -1) - int val = 0 ; - if (_type == _Freq_) { - if ((Freq*Range==0) && (_dirn==_Down)) { // Attempt to bump below lower limit - MarginVW[MWidth - MarginCount] = '\0' ; // Calculate padding required for command characters and cursor - strcpy(ResultStr,MarginVW) ; - strcat(ResultStr,"Error - Minimum Frequency\n") ; - } - // TBD - remove hardcoded Max frequency - else if ((Freq*Range==1000000) && (_dirn==_Up)) { // Attempt to bump above upper limit -// else if ((Freq*Range>=MaxDACfreq) && (_dirn==_Up)) { // Attempt to bump above upper limit - MarginVW[MWidth - MarginCount] = '\0' ; // Calculate padding required for command characters and cursor - strcpy(ResultStr,MarginVW) ; - strcat(ResultStr,"Error - Maximum Frequency\n") ; - } - 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 - } - val = Freq ; - DACspeed(Freq * Range) ; } - } - if (_type == _Phase_) { - Phase += _dirn ; - if (Phase == 360) Phase = 0 ; // Top Endwrap - if (Phase < 0 ) Phase = 359 ; // Bottom Endwrap - val = Phase ; - 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 - val = Level ; - 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 - val = DutyC ; - 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 - val = RiseT ; - 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 - val = Harm ; - DataCalc(); } // Update Bitmap with new Sine harmonic value - return (val) ; - } - - void 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 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 - } - StatusString () ; // Update the terminal session - } - - void DataCalc () { - 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 % 10; // Sine harmonics cycles after 7 - for (i=0; i= 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 - 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 x1) { DAC_data[j] = (BitMapSize - 1) - ((i - x1) * g2); } // Falling section of waveform - } - break ; - } - StatusString () ; // Update the terminal session - } - -public: - // 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. - // 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. - // Constructor - int DAC_chan(char _name, PIO _pio, uint _GPIO) { - pio = _pio, GPIO = _GPIO, name = _name ; // Copy parameters to class vars - PIOnum = pio_get_index(pio) ; // Print friendly value - Funct = _Sine_, Freq = 100, Level = 50 ; // Start-up default values... - Range = 1, Harm = 0, DutyC = 50, RiseT = 50 ; - 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 - 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! - 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. - - return(StateMachine); - } -}; +void ClockModule(DAC DACobj[] ); // Forward declaration bool Repeating_Timer_Callback(struct repeating_timer *t) { // Routine called 5 times per second... int i, steps=64, MidX=128, MidY=128 ; - // printf("%d\n",LEDCtr) ; // Debug - LEDCtr -- ; - if (LEDCtr>0) { + LEDctr -- ; + if (LEDctr>0) { // LED off, and no change to the time for 4 out of 5 cycles... - gpio_put(PICO_DEFAULT_LED_PIN, 0); // LED is connected to PICO_DEFAULT_LED_PIN - } else { + gpio_put(PICO_DEFAULT_LED_PIN, 0); // LED is connected to PICO_DEFAULT_LED_PIN + } else { // Falls through here once per second. - LEDCtr = 5 ; - gpio_put(PICO_DEFAULT_LED_PIN, 1); // LED is connected to PICO_DEFAULT_LED_PIN + LEDctr = 5 ; + gpio_put(PICO_DEFAULT_LED_PIN, 1); // LED is connected to PICO_DEFAULT_LED_PIN // Bump the clock... - if ((++Secs)>59) Secs=0 ; // Always bump seconds - if (Secs==0) { if ((++Mins)>59 ) Mins=0 ; } // Bump minutes when seconds = 0 - if ((Mins==0) && (Secs==0)) { if ((++Hours)>24) Hours=0 ; } // Bump hours when minutes and seconds = 0 + if ((++Secs)>59) Secs=0 ; // Always bump seconds + if (Secs==0) { if ((++Mins)>59 ) Mins=0 ; } // Bump minutes when seconds = 0 + if ((Mins==0) && (Secs==0)) { if ((++Hours)>24) Hours=0 ; } // Bump hours when minutes and seconds = 0 // Calculate seconds hand... - i=0, Radius=127 ; // Radius=Length of seconds hand - Angle=270-(Secs*6) ; // Angle in degrees, shifted 90 degree anti-clockwise - Radians=Angle*3.14159/180 ; // Angle in radians + i=0, Radius=127 ; // Radius=Length of seconds hand + Angle=270-(Secs*6) ; // Angle in degrees, shifted 90 degree anti-clockwise + Radians=Angle*3.14159/180 ; // Angle in radians StartX=Radius*cos(Radians)+MidX ; StartY=Radius*sin(Radians)+MidY ; while(i12 - Radians=Angle*3.14159/180 ; // Angle in radians + Angle=5*(270-(((Hours%12)*6)+(Mins/12)%5)) ; // Angle in degrees, shifted 90 degree anti-clockwise, + // and scaled by 5 to provide range 0=>12 + Radians=Angle*3.14159/180 ; // Angle in radians StartX=Radius*cos(Radians)+MidX ; StartY=Radius*sin(Radians)+MidY ; while(isi - Sine wave (default = no harmonics)\n" - "%ssin - Sine wave +nth harmonic ( 0->9 )\n" - "%ssi+ - Sine wave harmonic + 1\n" - "%ssi- - Sine wave harmonic - 1\n" - "%ssq - Square wave (default = 50%%%% duty cycle)\n" - "%ssqnnn - Square wave with nnn%%%% duty cycle\n" - "%ssq+ - Square wave duty cycle + 1%%%%\n" - "%ssq- - Square wave duty cycle - 1%%%%\n" - "%str - Triangle wave\n" - "%strnnn - Triangle wave with nnn%%%% rise time\n" - "%str+ - Triangle wave rise time + 1%%%%\n" - "%str- - Triangle wave rise time - 1%%%%\n" - "%ssw - Sweep frequency (Low, High, Speed, Pause)\n" - "%sfrnnn - Frequency = nnn ( 0->999 )\n" - "%sfr+ - Frequency + 1\n" - "%sfr- - Frequency - 1\n" - "%sphnnn - Phase = nnn ( 0->359 degrees )\n" - "%sph+ - Phase + 1\n" - "%sph- - Phase - 1\n" - "%slennn - Level = nnn ( 0->100%%%% )\n" - "%sle+ - Level + 1\n" - "%sle- - Level - 1\n" - "%sti - Time mode (display analog clock)\n" - "%swhere...\n" - "%s = DAC channel A,B or C=both\n" - "%snnn = Three digit numeric value\n", - MarginVW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, - MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, - MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, - MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, - MarginFW ) ; + sprintf(_Result.Txt, "%sHelp...\n" + "%s? - Help\n" + "%sV - Version\n" + "%sI - Info\n" + "%sS - Status\n" + "%sCl - Clock mode\n" + "%sD1 - Demo #01\n" + "%ssi - Sine wave (default = no harmonics)\n" + "%ssin - Sine wave +nth harmonic ( 0->9 )\n" + "%ssi+ - Sine wave harmonic + 1\n" + "%ssi- - Sine wave harmonic - 1\n" + "%ssq - Square wave (default = 50%%%% duty cycle)\n" + "%ssqnnn - Square wave with nnn%%%% duty cycle\n" + "%ssq+ - Square wave duty cycle + 1%%%%\n" + "%ssq- - Square wave duty cycle - 1%%%%\n" + "%str - Triangle wave\n" + "%strnnn - Triangle wave with nnn%%%% rise time\n" + "%str+ - Triangle wave rise time + 1%%%%\n" + "%str- - Triangle wave rise time - 1%%%%\n" + "%ssw - Sweep frequency (Low, High, Speed, Pause)\n" + "%sfrnnn - Frequency = nnn ( 0->999 )\n" + "%sfr+ - Frequency + 1\n" + "%sfr- - Frequency - 1\n" + "%sphnnn - Phase = nnn ( 0->359 degrees )\n" + "%sph+ - Phase + 1\n" + "%sph- - Phase - 1\n" + "%slennn - Level = nnn ( 0->100%%%% )\n" + "%sle+ - Level + 1\n" + "%sle- - Level - 1\n" + "%swhere...\n" + "%s\t = DAC channel A,B or C=both\n" + "%s\tnnn = Three digit numeric value\n", + MarginVW, Margin, Margin, Margin, Margin, Margin, Margin, Margin, + Margin, Margin, Margin, Margin, Margin, Margin, Margin, Margin, + Margin, Margin, Margin, Margin, Margin, Margin, Margin, Margin, + Margin, Margin, Margin, Margin, Margin, Margin, Margin, Margin, + Margin, Margin ) ; } void SysInfo (DAC DACobj[] ) { @@ -511,138 +154,155 @@ void SysInfo (DAC DACobj[] ) { // a) ResultStr is copied to outStr using sprintf - this reduces '%%%%' to '%%' // b) outStr is sent to terminal using printf - this reduces '%%' to '%' // 2) There seems to be some upper limit to sprintf, so the string is split into two smaller parts. + strcpy(MarginVW,Margin); // Re-initialise Variable Width margin... tmp = strlen(inStr) ; // Get number of command line characters - if (tmp != 0) tmp ++ ; // If there are characters, Bump to also allow for cursor + 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 + // First part of string... - sprintf(ResultStr,"%s|----------------------------------------------------------|\n" - "%s| System Info... |\n" - "%s|----------------------------------------------------------|\n" - "%s| Target board: Pico |\n" - "%s| RP2040 clock frequency: %7.3fMHz |\n" - "%s| Max DAC frequency: %7.3fMHz |\n" - "%s|----------------------------|-----------------------------|\n" - "%s| DAC Channel A | DAC Channel B |\n" - "%s|----------------------------|-----------------------------|\n" - "%s| Level: %3d%%%% | Level: %3d%%%% |\n" - "%s| Frequency: %3d | Frequency: %3d |\n" - "%s| Multiplier: %7d | Multiplier: %7d |\n" - "%s| Phase: %3d | Phase: %3d |\n" - "%s| Duty cycle: %3d%%%% | Duty cycle: %3d%%%% |\n" - "%s| Sine harmonic: %1d | Sine harmonic: %1d |\n" - "%s| Triangle Rise: %3d%%%% | Triangle Rise: %3d%%%% |\n", - MarginVW, MarginFW, MarginFW, MarginFW, - MarginFW, (float)clock_get_hz(clk_sys)/1000000, - MarginFW, MaxDACfreq/1000000, - MarginFW, MarginFW, MarginFW, - MarginFW, DACobj[_DAC_A].Level, DACobj[_DAC_B].Level, - MarginFW, DACobj[_DAC_A].Freq, DACobj[_DAC_B].Freq, - MarginFW, DACobj[_DAC_A].Range, DACobj[_DAC_B].Range, - MarginFW, DACobj[_DAC_A].Phase, DACobj[_DAC_B].Phase, - MarginFW, DACobj[_DAC_A].DutyC, DACobj[_DAC_B].DutyC, - MarginFW, DACobj[_DAC_A].Harm, DACobj[_DAC_B].Harm, - MarginFW, DACobj[_DAC_A].RiseT, DACobj[_DAC_B].RiseT + sprintf(_Result.Txt,"%s|----------------------------------------------------------|\n" + "%s| System Info... |\n" + "%s|----------------------------------------------------------|\n" + "%s| CPU clock frequency: %7.3fMHz |\n" + "%s| Max DAC frequency: %7.3fMHz |\n" + "%s|----------------------------|-----------------------------|\n" + "%s| DAC Channel A | DAC Channel B |\n" + "%s|----------------------------|-----------------------------|\n" + "%s| Level: %3d%%%% | Level: %3d%%%% |\n" + "%s| Frequency: %3d | Frequency: %3d |\n" + "%s| Multiplier: %7d | Multiplier: %7d |\n" + "%s| Phase: %3d | Phase: %3d |\n" + "%s| Duty cycle: %3d%%%% | Duty cycle: %3d%%%% |\n" + "%s| Sine harmonic: %1d | Sine harmonic: %1d |\n" + "%s| Triangle Rise: %3d%%%% | Triangle Rise: %3d%%%% |\n", + MarginVW, Margin, Margin, + Margin, (float)clock_get_hz(clk_sys)/1000000, + Margin, MaxDACfreq/1000000, + Margin, Margin, Margin, + Margin, DACobj[_DAC_A].Level, DACobj[_DAC_B].Level, + Margin, DACobj[_DAC_A].Freq, DACobj[_DAC_B].Freq, + Margin, DACobj[_DAC_A].Range, DACobj[_DAC_B].Range, + Margin, DACobj[_DAC_A].Phase, DACobj[_DAC_B].Phase, + Margin, DACobj[_DAC_A].DutyC, DACobj[_DAC_B].DutyC, + Margin, DACobj[_DAC_A].Harm, DACobj[_DAC_B].Harm, + Margin, DACobj[_DAC_A].RiseT, DACobj[_DAC_B].RiseT ) ; - printf(ResultStr) ; // Print first part of string + printf(_Result.Txt) ; // Print first part of string // Second part of string... - sprintf(ResultStr,"%s|----------------------------|-----------------------------|\n" - "%s| Divider: %10.3f | Divider: %10.3f |\n" - "%s|----------------------------|-----------------------------|\n" - "%s| PIO: %d | PIO: %d |\n" - "%s| State machine: %d | State machine: %d |\n" - "%s| GPIO: %d->%d | GPIO: %d->%d |\n" - "%s| *BM size: %8d | *BM size: %8d |\n" - "%s| *BM start: %x | *BM start: %x |\n" - "%s| Wrap Bottom: %2x | Wrap Bottom: %2x |\n" - "%s| Wrap Top: %2x | Wrap Top: %2x |\n" - "%s| DMA ctrl: %2d | DMA ctrl: %2d |\n" - "%s| DMA data: %2d | DMA data: %2d |\n" - "%s|----------------------------|-----------------------------|\n" - "%s *BM = Bit map\n", - MarginFW, - MarginFW, DACobj[_DAC_A].DAC_div, DACobj[_DAC_B].DAC_div, - MarginFW, - MarginFW, DACobj[_DAC_A].PIOnum, DACobj[_DAC_B].PIOnum, - MarginFW, DACobj[_DAC_A].StateMachine, DACobj[_DAC_B].StateMachine, - MarginFW, DACobj[_DAC_A].GPIO, DACobj[_DAC_A].GPIO+7, + sprintf(_Result.Txt,"%s|----------------------------|-----------------------------|\n" + "%s| Divider: %10.3f | Divider: %10.3f |\n" + "%s|----------------------------|-----------------------------|\n" + "%s| PIO: %d | PIO: %d |\n" + "%s| State machine: %d | State machine: %d |\n" + "%s| GPIO: %d->%d | GPIO: %d->%d |\n" + "%s| *BM size: %8d | *BM size: %8d |\n" + "%s| *BM start: %x | *BM start: %x |\n" + "%s| Wrap Bottom: %2x | Wrap Bottom: %2x |\n" + "%s| Wrap Top: %2x | Wrap Top: %2x |\n" + "%s| DMA ctrl: %2d | DMA ctrl: %2d |\n" + "%s| DMA data: %2d | DMA data: %2d |\n" + "%s|----------------------------|-----------------------------|\n" + "%s *BM = Bit map\n", + Margin, + Margin, DACobj[_DAC_A].DAC_div, DACobj[_DAC_B].DAC_div, + Margin, + Margin, DACobj[_DAC_A].PIOnum, DACobj[_DAC_B].PIOnum, + Margin, DACobj[_DAC_A].StateMachine, DACobj[_DAC_B].StateMachine, + Margin, DACobj[_DAC_A].GPIO, DACobj[_DAC_A].GPIO+7, DACobj[_DAC_B].GPIO, DACobj[_DAC_B].GPIO+7, - MarginFW, BitMapSize, BitMapSize, - MarginFW, (int)&DACobj[_DAC_A].DAC_data[0], + Margin, BitMapSize, BitMapSize, + Margin, (int)&DACobj[_DAC_A].DAC_data[0], (int)&DACobj[_DAC_B].DAC_data[0], - MarginFW, DACobj[_DAC_A].SM_WrapBot, DACobj[_DAC_B].SM_WrapBot, - MarginFW, DACobj[_DAC_A].SM_WrapTop, DACobj[_DAC_B].SM_WrapTop, - MarginFW, DACobj[_DAC_A].ctrl_chan, DACobj[_DAC_B].ctrl_chan, - MarginFW, DACobj[_DAC_A].data_chan, DACobj[_DAC_B].data_chan, - MarginFW, MarginFW + Margin, DACobj[_DAC_A].SM_WrapBot, DACobj[_DAC_B].SM_WrapBot, + Margin, DACobj[_DAC_A].SM_WrapTop, DACobj[_DAC_B].SM_WrapTop, + Margin, DACobj[_DAC_A].ctrl_chan, DACobj[_DAC_B].ctrl_chan, + Margin, DACobj[_DAC_A].data_chan, DACobj[_DAC_B].data_chan, + Margin, Margin ) ; // Printing the final part of the string is handled by the calling routine. // This prevents the 'unknown command' mechanism from triggering. } -static inline 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"); -} - -static inline void cs_deselect(int _gpio) { - asm volatile("nop \n nop \n nop"); - gpio_put(_gpio, 1); - asm volatile("nop \n nop \n nop"); -} - -static 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); -} - -static void MCP41020_Write (uint8_t _ctrl, uint8_t _data) { - // Add a control bit to select a 'Digi-Pot Write' command. - // Scale the data byte to be in the range 0->255. - // Transmit data over the SPI bus to the Digi-Pot. - uint8_t buff[2]; - buff[0] = _ctrl | 0x10 ; // Set command 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) ; -} - -static void getLine() { - char *pPos = (char *)inStr ; // Pointer to start of Global input string +void getLine() { + char *pPos = (char *)inStr ; // Pointer to start of Global input string int count = 0 ; while(1) { c = getchar(); - if (c == eof || c == '\n' || c == '\r') break ; // Non blocking exit - putchar(c); // FullDuplex echo - *pPos++ = c ; // Bump pointer, store character + if (c == eof || c == '\n' || c == '\r') break ; // Non blocking exit + putchar(c); // FullDuplex echo + *pPos++ = c ; // Bump pointer, store character count ++ ; } *pPos = '\0' ; - MarginCount += count ; // Track number of characters on current line return ; } -int SetVal(DAC DACobj[], int _Parm) { - // Common code for setting frequency, duty cycle, phase, waveform and level. - // Handles options to set a specific value or bump up/down... - if (inStr[3] == '+') { // Bump up and grab result for SPI display... - if (SelectedChan & 0b01) result = DACobj[_DAC_A].Bump(_Parm,_Up); - if (SelectedChan & 0b10) result = DACobj[_DAC_B].Bump(_Parm,_Up) ; - } else if (inStr[3] == '-') { // Bump down and grab result for SPI display... - if (SelectedChan & 0b01) result = DACobj[_DAC_A].Bump(_Parm,_Down) ; - if (SelectedChan & 0b10) result = DACobj[_DAC_B].Bump(_Parm,_Down) ; - } else { // Not a bump, so set the absolute value from Parm[0]... - if (SelectedChan & 0b01) result = DACobj[_DAC_A].Set(_Parm,Parm[0]) ; - if (SelectedChan & 0b10) result = DACobj[_DAC_B].Set(_Parm,Parm[0]) ; +void Sweep(DAC DACobj[]) { + // Continual loop providing the frequency sweep function... + // Parm[0]=Low frequency, Parm[1]=High frequency, Parm[2]=Scan speed, Parm[3]=Low/High pause + i = Parm[0]; + for (;;) { + if (SelectedChan & 0b01) DACobj[_DAC_A].Set(_Freq_,i) ; // Set frequency, display status + if (SelectedChan & 0b10) DACobj[_DAC_B].Set(_Freq_,i) ; // Set frequency, display status + dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels... + printf(_Result.Txt) ; // Update terminal + _Result.Txt[0] = '\0' ; // Reset the string variable + SPI_Display_Write(i); // Update SPI display + if (i==Parm[0]) { dirn = 1; + printf("pause %dms\n",Parm[3]); + sleep_ms(Parm[3]); } + if (i>=Parm[1]) { dirn =-1; + printf("pause %dms\n",Parm[3]); + sleep_ms(Parm[3]); } + // 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); + + // 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 + i = i + dirn; + c = getchar_timeout_us (0); // Non-blocking char input + if ((c>=32) & (c<=126)) { + strcpy(_Result.Txt," Exit sweep mode\n") ; // Prevents error message + break; } // exit on keypress + sleep_ms(Parm[2]); // Speed of scan + } +} + +int SetVal(DAC DACobj[], int _Parm, int _Value) { +// Common code for setting DAC operating parameters. +// Handles options to set a specified values, or bump up/down... +// Parameters... +// DACobj = the selected DAC ( 0/1 or A/B ) +// _Parm = the required function (frequency, duty cycle, phase, waveform, level) + strcpy(_Result.Txt, Margin); // Initialise margin string + if (inStr[3] == '+') { // Bump up and grab result for SPI display... + _Result.Txt[MWidth-strlen(inStr)-1]= '\0'; // Truncate margin to allow for typed characters + if (SelectedChan & 0b01) DACobj[_DAC_A].Bump(_Parm,_Up); + if (inStr[0] == 'C') strcat(_Result.Txt,Margin); // If there are two lines, add second margin + if (SelectedChan & 0b10) DACobj[_DAC_B].Bump(_Parm,_Up); + } else if (inStr[3] == '-') { // Bump down and grab result for SPI display... + _Result.Txt[MWidth-strlen(inStr)-1]= '\0'; // Truncate margin to allow for typed characters + if (SelectedChan & 0b01) DACobj[_DAC_A].Bump(_Parm,_Down); + if (inStr[0] == 'C') strcat(_Result.Txt,Margin); // If there are two lines, add second margin + if (SelectedChan & 0b10) DACobj[_DAC_B].Bump(_Parm,_Down); + } else { // Not a bump, so set the absolute value from Parm[0]... + _Result.Txt[MWidth-strlen(inStr)-1]= '\0'; // Truncate margin to allow for typed characters + _Result.Val = Parm[0]; // Grab numeric value for SPI display + if (SelectedChan & 0b01) DACobj[_DAC_A].Set(_Parm,_Value); + if (inStr[0] == 'C') strcat(_Result.Txt,Margin); // If there are two lines, add second margin + if (SelectedChan & 0b10) DACobj[_DAC_B].Set(_Parm,_Value); } // 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); @@ -656,75 +316,63 @@ int SetVal(DAC DACobj[], int _Parm) { 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 - return result ; + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + + return _Value; } - int main() { - bool InvX=false, InvY=false ; // Clock display mode flags to allow inverted output - set_sys_clock_khz(SysClock*1000, true) ; // Set Pico clock speed - MaxDACfreq = clock_get_hz(clk_sys) / BitMapSize ; // Calculate Maximum DAC output frequency for given CPU clock speed - stdio_init_all() ; +int main() { + set_sys_clock_khz(SysClock*1000, true) ; // Set Pico clock speed + MaxDACfreq = clock_get_hz(clk_sys) / BitMapSize ; // Calculate Maximum DAC output frequency for given CPU clock speed + stdio_init_all(); + SPI_Init(PIN_CLK, PIN_TX); // Start the SPI bus - spi_init(SPI_PORT, 500000); // Set SPI0 at 0.5MHz... - gpio_set_function(PIN_CLK, GPIO_FUNC_SPI); - gpio_set_function(PIN_TX, GPIO_FUNC_SPI); - - gpio_init(Display_CS) ; // Initailse the required GPIO ports... + gpio_init(Display_CS) ; // Initailse the required GPIO ports... gpio_set_dir(Display_CS, GPIO_OUT) ; - gpio_put(Display_CS, 1) ; // Chip select is active-low, so initialise to high state + gpio_put(Display_CS, 1) ; // Chip select is active-low, so initialise to high state gpio_init(Level_CS) ; gpio_set_dir(Level_CS, GPIO_OUT) ; - gpio_put(Level_CS, 1) ; // Chip select is active-low, so initialise to high state + gpio_put(Level_CS, 1) ; // Chip select is active-low, so initialise to high state gpio_init(PICO_DEFAULT_LED_PIN) ; gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT) ; - gpio_set_dir(PIN_CLK, GPIO_OUT) ; // Initialise remaining SPI connections... - gpio_set_dir(PIN_TX, GPIO_OUT) ; - - for (int i=0; i<16; i++) { - gpio_set_slew_rate(i, GPIO_SLEW_RATE_FAST); // Setting Max slew rate and gpio drive strength keeps output - gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_12MA); // linear at high frequencies... + + for (int i=0; i<8; i++) { // Initialise GPIO ports for DMA channels... + gpio_set_dir(DAC_A_Start + i, GPIO_OUT); + gpio_set_slew_rate(DAC_A_Start + i, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(DAC_A_Start + i, GPIO_DRIVE_STRENGTH_12MA); + gpio_set_dir(DAC_B_Start + i, GPIO_OUT); + gpio_set_slew_rate(DAC_B_Start + i, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(DAC_B_Start + i, GPIO_DRIVE_STRENGTH_12MA); } - memset(MarginFW,' ',MWidth) ; // Initialise Fixed Width margin... - MarginFW[MWidth] = '\0' ; // ... and terminate - memset(MarginVW,' ',MWidth) ; // Initialise Variable Width margin... - MarginVW[MWidth] = '\0' ; // ... and terminate - ResultStr[0] = '\0' ; // Reset string + // Note: Need to instantiate DAC objects AFTER the Pico clock speed has been set... + DAC DACobj[2] { {'A',_PIOnum_,DAC_A_Start }, {'B',_PIOnum_,DAC_B_Start} }; // Define array to contain the two DAC channel objects + // and instantiate the two objects - // Instantiate objects to control the various State Machines... - // Note: Both DAC channels need to be on the same PIO to achieve - // Atomic restarts for accurate phase sync. - DAC DACobj[2]; // Array to hold the two DAC channel objects - DACobj[_DAC_A].DAC_chan('A',pio1,0); // First DAC channel object in array - resistor network connected to GPIO0->8 - DACobj[_DAC_B].DAC_chan('B',pio1,8); // Second DAC channel object in array - resistor network connected to GPIO8->16 + memset(Margin,' ',MWidth) ; // Initialise Fixed Width margin... + Margin[MWidth] = '\0' ; // ... and terminate - strcpy(LastCmd,"?") ; // Hitting return will give 'Help' + strcpy(LastCmd,"?") ; // Hitting return will give 'Help' - SPI_Display_Write(SysClock) ; // Pico system clock speed (in MHz) - MCP41020_Write(0x3, 50) ; // Both channels -> 50% output level + SPI_Display_Write(SysClock) ; // Pico system clock speed (in MHz) + MCP41020_Write(0x3, 50) ; // Both channels -> 50% output level - while (!stdio_usb_connected()) { sleep_ms(100); } // Wait for USB connection... - - SPI_Display_Write(DACobj[_DAC_A].Freq) ; // Frequency => SPI display - - // Send (optional) start-up messages to terminal... - VerText() ; // Version text - printf(ResultStr) ; // Update terminal + // Optional start-up behaviour... + // while (!stdio_usb_connected()) { sleep_ms(100); } // Wait for USB connection + VerText() ; // Start-up message to terminal + printf(_Result.Txt) ; // Atomic Restart - starting all 4 DMA channels simultaneously ensures phase sync between both DAC channels - dma_start_channel_mask(DAC_channel_mask); // Sets the 'Busy' flag in Ctrl reg + dma_start_channel_mask(DAC_channel_mask); // Sets the 'Busy' flag in Ctrl reg - struct repeating_timer timer; add_repeating_timer_ms(-200, Repeating_Timer_Callback, NULL, &timer) ; // 5 x per second to blink LED while(1) { ParmCnt=0, Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0 ; // Reset all command line parameters - memset(MarginVW,' ',MWidth) ; // Re-initialise Variable Width margin... + strcpy(MarginVW,Margin); // Re-initialise Variable Width margin... MarginVW[MWidth] = '\0' ; // ... and terminate - ResultStr[0] = '\0' ; // Reset string + _Result.Txt[0] = '\0' ; // Reset string printf(">") ; // Command prompt - MarginCount = 1 ; // Reset count and bump for command prompt getLine() ; // Fetch command line @@ -732,15 +380,25 @@ int SetVal(DAC DACobj[], int _Parm) { if (strlen(inStr) == 0) { strcpy(inStr,LastCmd) ; // Repeat last command printf("%s", inStr) ; } - // One character commands... if (strlen(inStr) == 1) { + // One character commands... if (inStr[0] == '?') HlpText() ; // Help text if (inStr[0] == 'V') VerText() ; // Version text if (inStr[0] == 'S') { - DACobj[_DAC_A].StatusString() ; - DACobj[_DAC_B].StatusString() ; + strcpy(_Result.Txt, Margin); + _Result.Txt[MWidth-2] = '\0' ; // Short margin due to existing input char + strcat(_Result.Txt, DACobj[_DAC_A].StatusString()) ; // Can be one or two line result + strcat(_Result.Txt, Margin); // Full margin due to no input char + strcat(_Result.Txt, DACobj[_DAC_B].StatusString()) ; } - if (inStr[0] == 'I') SysInfo(DACobj); // TBD - inconsitant - make these global ?? + if (inStr[0] == 'I') SysInfo(DACobj); // Info + } + + if (strlen(inStr) == 2) { + // Two character commands... + if ((inStr[0]=='C')&(inStr[1]=='l')) ClockModule(DACobj) ; // Clock display + if ((inStr[0]=='D')&(inStr[1]=='1')) Demo_01(DACobj) ; // Demo #1 - Sine, Triangle, Square + if ((inStr[0]=='D')&(inStr[1]=='2')) Demo_02(DACobj) ; // Demo #2 - Lissajous } // For all remaining commands, the first character selects DAC channel A or B... @@ -770,168 +428,44 @@ int SetVal(DAC DACobj[], int _Parm) { // overwrite the 'M'. Writing a space to the next char prevents, this from happening. if (inStr[i+1] == 'H') inStr[i+1] = ' ' ; // Overwrite next char with space } - else if ( inStr[i] == ',' ) { ParmCnt++ ; } // Next parameter + else if ( inStr[i] == ',' ) { ParmCnt++ ; } // Next parameter 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 } } // Next two chars select the command... - if ((inStr[1]=='p')&(inStr[2]=='h')) SetVal(DACobj,_Phase_) ; // Phase - if ((inStr[1]=='l')&(inStr[2]=='e')) SetVal(DACobj,_Level_) ; // Level - if ((inStr[1]=='s')&(inStr[2]=='i')) SetVal(DACobj,_Sine_) ; // Sine wave (optional harmonic parameter) - if ((inStr[1]=='f')&(inStr[2]=='r')) SetVal(DACobj,_Freq_) ; // Frequency + if ((inStr[1]=='p')&(inStr[2]=='h')) SetVal(DACobj,_Phase_,Parm[0]) ; // Phase + if ((inStr[1]=='l')&(inStr[2]=='e')) SetVal(DACobj,_Level_,Parm[0]) ; // Level + if ((inStr[1]=='s')&(inStr[2]=='i')) SetVal(DACobj,_Sine_,Parm[0]) ; // Sine wave (optional harmonic parameter) + if ((inStr[1]=='f')&(inStr[2]=='r')) SetVal(DACobj,_Freq_,Parm[0]) ; // Frequency - // The next two commands need different default values... - if (strlen(inStr)==3) Parm[0] = 50 ; // If no value provided, set default to 50 - if ((inStr[1]=='s')&(inStr[2]=='q')) SetVal(DACobj,_Square_) ; // Set Square wave (optional duty cycle parameter) - if ((inStr[1]=='t')&(inStr[2]=='r')) SetVal(DACobj,_Triangle_) ; // Set Triangle wave (optional duty cycle parameter) + // The next two commands require different default values... + if (strlen(inStr)==3) Parm[0] = 50 ; // If no value provided, set default to 50 + if ((inStr[1]=='s')&(inStr[2]=='q')) SetVal(DACobj,_Square_,Parm[0]) ; // Set Square wave (optional duty cycle parameter) + if ((inStr[1]=='t')&(inStr[2]=='r')) SetVal(DACobj,_Triangle_,Parm[0]) ; // Set Triangle wave (optional duty cycle parameter) - if ((inStr[1]=='t')&(inStr[2]=='i')) { // Time display... - // 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... + if ((inStr[1] == 's') & (inStr[2] == 'w')) Sweep(DACobj) ; // Sweep display mode - // 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(pio1,0,false) ; // disable State machine 0 !! HARD CODED !! - pio_sm_set_enabled(pio1,1,false) ; // disable State machine 1 - - for (uint i=0; i<16; i++) { // Grab the GPIO back from the State machines - gpio_init(i); - gpio_set_dir(i, GPIO_OUT); - } - gpio_clr_mask(0xff) ; // clear first 16 GPIO outputs - - ResultStr[0] = '\0' ; // String also used as a flag, so needs to be cleared - while (ResultStr[0] == '\0') { // exit on keypress - float Radians ; - int outX, outY ; - // Draw the clock face... - for (int i=0; i",MarginVW) ; // Print current status - else printf("%sX axis not inverted.\n>",MarginVW) ; - } - else if ((c=='y') or (c=='Y')) { - InvY = !InvY ; - if (InvY) printf("%sY axis inverted.\n>",MarginVW) ; // Print current status - else printf("%sY axis not inverted.\n>",MarginVW) ; - } - if ((c=='S') or (c=='s')) { // Set time - printf("%sSet time (format HH:MM:SS)\n%s",MarginVW, MarginFW ) ; - getLine() ; // Get the console input - Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0 ; // Reset all command line parameters - i=0, ParmCnt=0 ; // Reset all command line counters - while (i",MarginFW,Hours,Mins,Secs) ; - } - else if ((c=='q') or (c=='Q')) { - for (uint i=0; i<16; i++) { pio_gpio_init(pio1, i); } // Hand the GPIO's back to the state machines - - // 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(pio1,0,true) ; // Re-enable State machine 0 !! HARD CODED !! - pio_sm_set_enabled(pio1,1,true) ; // Re-enable State machine 1 - dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels - - strcpy(ResultStr," Quit clock mode\n") ; // Prevents error message - } - } - } - } - - // The final command is a continual loop creating the sweep function... - if ((inStr[1] == 's') & (inStr[2] == 'w')) { // Sweep - // Parm[0]=Low frequency, Parm[1]=High frequency, Parm[2]=Scan speed, Parm[3]=Low/High pause - i = Parm[0]; - for (;;) { - if (SelectedChan & 0b01) result = DACobj[_DAC_A].Set(_Freq_,i) ; // Set frequency, display status - if (SelectedChan & 0b10) result = DACobj[_DAC_B].Set(_Freq_,i) ; // Set frequency, display status - dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels... - printf(ResultStr) ; // Update terminal - ResultStr[0] = '\0' ; // Reset the string variable - SPI_Display_Write(i); // Update SPI display - if (i==Parm[0]) { dirn = 1; - sleep_ms(Parm[3]); } - if (i>=Parm[1]) { dirn =-1; - sleep_ms(Parm[3]); } - // 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); - - dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels - i = i + dirn; - c = getchar_timeout_us (0); // Non-blocking char input - if ((c>=32) & (c<=126)) { - strcpy(ResultStr," Exit sweep mode\n") ; // Prevents error message - break; } // exit on keypress - sleep_ms(Parm[2]); // Speed of scan - } - } - - if (strlen(ResultStr) == 0) { // No result can only mean unrecognised command - strcpy(MarginVW,MarginFW) ; // Reset Variable Width margin - tmp = strlen(inStr) ; + // All successful commands create a ResultStr, so the abscence of a ResultStr indicates an unrecognised command + if (*_Result.Txt == '\0') { // Check for empty string + tmp = strlen(inStr) ; // Deffo garbage input , but we'll align it nicely regardless if (tmp != 0) tmp ++ ; // Bump to allow for cursor character - MarginVW[MWidth - tmp] = '\0' ; // Calculate padding for input and cursor - sprintf(outStr,"%sUnknown command!\n", MarginVW) ; // Empty response buffer indicates command has not been recognised + strcpy(_Result.Txt, Margin); + _Result.Txt[MWidth - tmp] = '\0' ; // Calculate padding for input and cursor + strcat(_Result.Txt,"Unknown command!\n"); } - else strcpy(outStr,ResultStr) ; - printf(outStr) ; // Update terminal - outStr[0] = '\0' ; // Clear (reset) the string variable - SPI_Display_Write(result) ; // Update SPI display + + printf(_Result.Txt) ; // Update terminal + SPI_Display_Write(_Result.Val) ; // Update SPI display device + strcpy(LastCmd, inStr) ; // Preserve last command + inStr[0] = '\0' ; // Reset input string + outStr[0] = '\0' ; // Clear (reset) the string variable + + // Serial comms causes phase drift at high frequencies, particularly when using serial over USB. + // This is mitigated by reseting phase lock after any serial comms activity... + PhaseLock(DACobj); // Phase lock the DAC channels } return 0; } diff --git a/Function Generator/FunctionGenerator.uf2 b/Function Generator/FunctionGenerator.uf2 deleted file mode 100644 index a48d677..0000000 Binary files a/Function Generator/FunctionGenerator.uf2 and /dev/null differ diff --git a/Function Generator/Function_Generator.uf2 b/Function Generator/Function_Generator.uf2 new file mode 100644 index 0000000..4d11ec0 Binary files /dev/null and b/Function Generator/Function_Generator.uf2 differ diff --git a/Function Generator/GPIO+Definitions.h b/Function Generator/GPIO+Definitions.h new file mode 100644 index 0000000..278e236 --- /dev/null +++ b/Function Generator/GPIO+Definitions.h @@ -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 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/Function Generator/SPI_Utils.cpp b/Function Generator/SPI_Utils.cpp new file mode 100644 index 0000000..a2491fd --- /dev/null +++ b/Function Generator/SPI_Utils.cpp @@ -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) ; +} diff --git a/Function Generator/SPI_Utils.h b/Function Generator/SPI_Utils.h new file mode 100644 index 0000000..a251c2b --- /dev/null +++ b/Function Generator/SPI_Utils.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#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); diff --git a/Function Generator/pico_sdk_import.cmake b/Function Generator/pico_sdk_import.cmake new file mode 100644 index 0000000..a0721d0 --- /dev/null +++ b/Function Generator/pico_sdk_import.cmake @@ -0,0 +1,84 @@ +# This is a copy of /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})