├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── __init__.py ├── build.sh ├── gpiod_callback.c ├── gpiod_callback.h ├── lm4811.c ├── lm4811.h ├── lm4811_set_volume.c ├── mcp4728_set_address.c ├── tpa6130.c ├── tpa6130.h ├── tpa6130_set_volume.c ├── wiringPiI2C.c ├── wiringPiI2C.h ├── zynads1115.c ├── zynads1115.h ├── zynaptik.c ├── zynaptik.h ├── zyncoder.c ├── zyncoder.h ├── zyncoder_test.c ├── zyncontrol.h ├── zyncontrol_mini_v2.c ├── zyncontrol_v5.c ├── zyncontrol_vx.c ├── zyncontrol_z2.c ├── zyncore.c ├── zyncore.py ├── zynmaster.c ├── zynmaster.h ├── zynmcp23008.c ├── zynmcp23008.h ├── zynmcp23017.c ├── zynmcp23017.h ├── zynmidirouter.c ├── zynmidirouter.h ├── zynmidirouter.txt ├── zynpot.c ├── zynpot.h ├── zynrv112.c ├── zynrv112.h ├── zyntof.c └── zyntof.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | #project(zyncore LANGUAGES CXX) 3 | project(zyncore) 4 | 5 | include(CheckIncludeFiles) 6 | include(CheckLibraryExists) 7 | 8 | link_directories(/usr/local/lib) 9 | 10 | if (("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_ENCODERS") 11 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_ENCODERS_V5TOUCH") 12 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_EXTRA") 13 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_EPDF") 14 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_EPDF_REVERSE") 15 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_ZynScreen") 16 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_ZynScreen_Zynaptik") 17 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_ZynScreen_Zynface") 18 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_Zynaptik-3") 19 | OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MCP23017_Zynaptik-3_Zynface")) 20 | message("++ Defined MCP23017_ENCODERS") 21 | add_definitions(-DMCP23017_ENCODERS) 22 | 23 | if (DEFINED ENV{ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS}" STREQUAL "")) 24 | message("++ Defined MCP23017_I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS}") 25 | add_definitions(-DMCP23017_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_MCP23017_I2C_ADDRESS}) 26 | endif() 27 | 28 | if (DEFINED ENV{ZYNTHIAN_WIRING_MCP23017_INTA_PIN} AND NOT ("$ENV{ZYNTHIAN_WIRING_MCP23017_INTA_PIN}" STREQUAL "")) 29 | message("++ Defined MCP23017_INTA_PIN $ENV{ZYNTHIAN_WIRING_MCP23017_INTA_PIN}") 30 | add_definitions(-DMCP23017_INTA_PIN=$ENV{ZYNTHIAN_WIRING_MCP23017_INTA_PIN}) 31 | endif() 32 | 33 | if (DEFINED ENV{ZYNTHIAN_WIRING_MCP23017_INTB_PIN} AND NOT ("$ENV{ZYNTHIAN_WIRING_MCP23017_INTB_PIN}" STREQUAL "")) 34 | message("++ Defined MCP23017_INTB_PIN $ENV{ZYNTHIAN_WIRING_MCP23017_INTB_PIN}") 35 | add_definitions(-DMCP23017_INTB_PIN=$ENV{ZYNTHIAN_WIRING_MCP23017_INTB_PIN}) 36 | endif() 37 | 38 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}" STREQUAL "")) 39 | message("++ Defined ZYNAPTIK_CONFIG $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}") 40 | add_definitions(-DZYNAPTIK_CONFIG="$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}") 41 | set(BUILD_ZYNAPTIK "1") 42 | 43 | if ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}" MATCHES "^Custom") 44 | message("++ Defined ZYNAPTIK_VERSION 1") 45 | add_definitions(-DZYNAPTIK_VERSION=1) 46 | elseif("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}" MATCHES "^Zynaptik-2") 47 | message("++ Defined ZYNAPTIK_VERSION 2") 48 | add_definitions(-DZYNAPTIK_VERSION=2) 49 | elseif("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}" MATCHES "^Zynaptik-3") 50 | message("++ Defined ZYNAPTIK_VERSION 3") 51 | add_definitions(-DZYNAPTIK_VERSION=3) 52 | elseif("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}" MATCHES "^Zynface-V5") 53 | message("++ Defined ZYNAPTIK_VERSION 5") 54 | add_definitions(-DZYNAPTIK_VERSION=5) 55 | endif() 56 | endif() 57 | 58 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_I2C_ADDRESS}" STREQUAL "")) 59 | message("++ Defined ZYNAPTIK_MCP23017_I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_I2C_ADDRESS}") 60 | add_definitions(-DZYNAPTIK_MCP23017_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_I2C_ADDRESS}) 61 | endif() 62 | 63 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTA_PIN} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTA_PIN}" STREQUAL "")) 64 | message("++ Defined ZYNAPTIK_MCP23017_INTA_PIN $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTA_PIN}") 65 | add_definitions(-DZYNAPTIK_MCP23017_INTA_PIN=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTA_PIN}) 66 | endif() 67 | 68 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTB_PIN} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTB_PIN}" STREQUAL "")) 69 | message("++ Defined ZYNAPTIK_MCP23017_INTB_PIN $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTB_PIN}") 70 | add_definitions(-DZYNAPTIK_MCP23017_INTB_PIN=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP23017_INTB_PIN}) 71 | endif() 72 | 73 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}" STREQUAL "")) 74 | message("++ Defined ZYNAPTIK_ADS1115 I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}") 75 | add_definitions(-DZYNAPTIK_ADS1115_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}) 76 | endif() 77 | 78 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}" STREQUAL "")) 79 | message("++ Defined ZYNAPTIK_MCP4728 I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}") 80 | add_definitions(-DZYNAPTIK_MCP4728_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}) 81 | endif() 82 | 83 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNTOF_CONFIG} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNTOF_CONFIG}" STREQUAL "")) 84 | message("++ Defined ZYNTOF_CONFIG $ENV{ZYNTHIAN_WIRING_ZYNTOF_CONFIG}") 85 | add_definitions(-DZYNTOF_CONFIG="$ENV{ZYNTHIAN_WIRING_ZYNTOF_CONFIG}") 86 | set(BUILD_ZYNTOF "1") 87 | endif() 88 | 89 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}" STREQUAL "")) 90 | message("++ Defined ZYNAPTIK_CVIN_VOLTS_OCTAVE $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}") 91 | add_definitions(-DZYNAPTIK_CVIN_VOLTS_OCTAVE=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}) 92 | endif() 93 | 94 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}" STREQUAL "")) 95 | message("++ Defined ZYNAPTIK_CVOUT_VOLTS_OCTAVE $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}") 96 | add_definitions(-DZYNAPTIK_CVOUT_VOLTS_OCTAVE=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}) 97 | endif() 98 | 99 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}" STREQUAL "")) 100 | message("++ Defined ZYNAPTIK_CVIN_NOTE0 $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}") 101 | add_definitions(-DZYNAPTIK_CVIN_NOTE0=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}) 102 | endif() 103 | 104 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}" STREQUAL "")) 105 | message("++ Defined ZYNAPTIK_CVOUT_NOTE0 $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}") 106 | add_definitions(-DZYNAPTIK_CVOUT_NOTE0=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}) 107 | endif() 108 | 109 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V1")) 110 | message("++ Defined MCP23017_ENCODERS") 111 | add_definitions(-DMCP23017_ENCODERS) 112 | add_definitions(-DZ2_VERSION=1) 113 | 114 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V2")) 115 | message("++ Defined MCP23017_ENCODERS") 116 | add_definitions(-DMCP23017_ENCODERS) 117 | add_definitions(-DZ2_VERSION=2) 118 | 119 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V3")) 120 | message("++ Defined MCP23017_ENCODERS") 121 | add_definitions(-DMCP23017_ENCODERS) 122 | add_definitions(-DZ2_VERSION=3) 123 | 124 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "V5")) 125 | message("++ Defined MCP23017_ENCODERS") 126 | add_definitions(-DMCP23017_ENCODERS) 127 | add_definitions(-DV5_VERSION=1) 128 | 129 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MINI_V2")) 130 | message("++ Defined MCP23017_ENCODERS") 131 | add_definitions(-DMCP23017_ENCODERS) 132 | add_definitions(-DMINI_VERSION=2) 133 | 134 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "V5_ZYNFACE")) 135 | message("++ Defined MCP23017_ENCODERS") 136 | add_definitions(-DMCP23017_ENCODERS) 137 | message("++ Defined ZYNAPTIK_CONFIG $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}") 138 | add_definitions(-DZYNAPTIK_CONFIG="$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CONFIG}") 139 | message("++ Defined ZYNAPTIK_VERSION 5") 140 | add_definitions(-DZYNAPTIK_VERSION=5) 141 | add_definitions(-DZYNAPTIK_MCP23017_BASE_PIN=400) 142 | add_definitions(-DZYNAPTIK_MCP23017_I2C_ADDRESS=0x22) 143 | add_definitions(-DZYNAPTIK_MCP23017_INTA_PIN=24) 144 | add_definitions(-DZYNAPTIK_MCP23017_INTB_PIN=23) 145 | message("++ Default ZYNAPTIK_MCP23017_I2C_ADDRESS 0x22") 146 | message("++ Default ZYNAPTIK_MCP23017_INTA_PIN 24") 147 | message("++ Default ZYNAPTIK_MCP23017_INTB_PIN 23") 148 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}" STREQUAL "")) 149 | message("++ Defined ZYNAPTIK_ADS1115_I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}") 150 | add_definitions(-DZYNAPTIK_ADS1115_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_ADS1115_I2C_ADDRESS}) 151 | endif() 152 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}" STREQUAL "")) 153 | message("++ Defined ZYNAPTIK_MCP4728_I2C_ADDRESS $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}") 154 | add_definitions(-DZYNAPTIK_MCP4728_I2C_ADDRESS=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_MCP4728_I2C_ADDRESS}) 155 | endif() 156 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}" STREQUAL "")) 157 | message("++ Defined ZYNAPTIK_CVIN_VOLTS_OCTAVE $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}") 158 | add_definitions(-DZYNAPTIK_CVIN_VOLTS_OCTAVE=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_VOLTS_OCTAVE}) 159 | endif() 160 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}" STREQUAL "")) 161 | message("++ Defined ZYNAPTIK_CVOUT_VOLTS_OCTAVE $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}") 162 | add_definitions(-DZYNAPTIK_CVOUT_VOLTS_OCTAVE=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_VOLTS_OCTAVE}) 163 | endif() 164 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}" STREQUAL "")) 165 | message("++ Defined ZYNAPTIK_CVIN_NOTE0 $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}") 166 | add_definitions(-DZYNAPTIK_CVIN_NOTE0=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVIN_NOTE0}) 167 | endif() 168 | if (DEFINED ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0} AND NOT ("$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}" STREQUAL "")) 169 | message("++ Defined ZYNAPTIK_CVOUT_NOTE0 $ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}") 170 | add_definitions(-DZYNAPTIK_CVOUT_NOTE0=$ENV{ZYNTHIAN_WIRING_ZYNAPTIK_CVOUT_NOTE0}) 171 | endif() 172 | set(BUILD_ZYNAPTIK "1") 173 | add_definitions(-DV5_VERSION=1) 174 | 175 | elseif(("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "DUMMIES") OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "TOUCH_ONLY")) 176 | message("++ Defined DUMMY_ENCODERS") 177 | add_definitions(-DDUMMY_ENCODERS) 178 | 179 | else() 180 | message("++ Defined MCP23008_ENCODERS") 181 | add_definitions(-DMCP23008_ENCODERS) 182 | endif() 183 | 184 | if (DEFINED ENV{TPA6130_KERNEL_DRIVER_LOADED} AND ("$ENV{TPA6130_KERNEL_DRIVER_LOADED}" STREQUAL "1")) 185 | message("++ TPA6130 kernel driver LOADED. Omitting user space driver.") 186 | else() 187 | message("++ TPA6130 kernel driver NOT LOADED. Using TPA6130 user space driver if needed.") 188 | add_definitions(-DTPA6130_DRIVER) 189 | endif() 190 | 191 | message("++ Building for Wiring Layout $ENV{ZYNTHIAN_WIRING_LAYOUT}") 192 | 193 | set_source_files_properties( zynrv112.c PROPERTIES LANGUAGE CXX LINKER_LANGUAGE CXX) 194 | 195 | if (("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V1") OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V2") OR ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "Z2_V3")) 196 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_z2.c lm4811.h lm4811.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 197 | target_link_libraries(zyncore gpiod jack lo) 198 | add_executable(lm4811_set_volume lm4811_set_volume.c lm4811.c gpiod_callback.h gpiod_callback.c) 199 | target_link_libraries(lm4811_set_volume gpiod pthread) 200 | 201 | elseif ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "V5") 202 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_v5.c tpa6130.c tpa6130.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 203 | target_link_libraries(zyncore gpiod jack lo) 204 | add_executable(tpa6130_set_volume tpa6130_set_volume.c tpa6130.c wiringPiI2C.h wiringPiI2C.c) 205 | target_link_libraries(tpa6130_set_volume gpiod) 206 | 207 | elseif ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "MINI_V2") 208 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_mini_v2.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 209 | target_link_libraries(zyncore gpiod jack lo) 210 | 211 | elseif ("$ENV{ZYNTHIAN_WIRING_LAYOUT}" STREQUAL "V5_ZYNFACE") 212 | message("++ Building Zynaptik support") 213 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_v5.c tpa6130.c tpa6130.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c zynaptik.h zynaptik.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 214 | target_link_libraries(zyncore gpiod jack MCP4728 lo) 215 | add_executable(tpa6130_set_volume tpa6130_set_volume.c tpa6130.c wiringPiI2C.h wiringPiI2C.c) 216 | target_link_libraries(tpa6130_set_volume gpiod) 217 | add_executable(mcp4728_set_address mcp4728_set_address.c) 218 | target_link_libraries(mcp4728_set_address MCP4728) 219 | 220 | else () 221 | if (BUILD_ZYNTOF AND BUILD_ZYNAPTIK) 222 | message("++ Building Zynaptik & Zyntof support") 223 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_vx.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c zynaptik.h zynaptik.c zyntof.h zyntof.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 224 | target_link_libraries(zyncore gpiod jack lo MCP4728 tof) 225 | elseif (BUILD_ZYNAPTIK) 226 | message("++ Building Zynaptik support") 227 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_vx.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c zynaptik.h zynaptik.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 228 | target_link_libraries(zyncore gpiod jack MCP4728 lo) 229 | elseif (BUILD_ZYNTOF) 230 | message("++ Building Zyntof support") 231 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_vx.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c zyntof.h zyntof.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 232 | target_link_libraries(zyncore gpiod jack lo tof) 233 | else() 234 | add_library(zyncore SHARED zyncore.c zyncontrol.h zyncontrol_vx.c zynpot.h zynpot.c zynrv112.h zynrv112.c zynads1115.h zynads1115.c zynmcp23017.h zynmcp23017.c zynmcp23008.h zynmcp23008.c zyncoder.h zyncoder.c zynmidirouter.h zynmidirouter.c zynmaster.h zynmaster.c wiringPiI2C.h wiringPiI2C.c gpiod_callback.h gpiod_callback.c) 235 | target_link_libraries(zyncore gpiod jack lo) 236 | endif() 237 | 238 | endif() 239 | 240 | add_executable(zyncoder_test zyncoder_test.c) 241 | target_link_libraries(zyncoder_test zyncore) 242 | 243 | install(TARGETS zyncore LIBRARY DESTINATION lib) 244 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zyncoder library 2 | 3 | [Zynthian](http://zynthian.org) is an Open Synth Platform based in Raspberry Pi, Linux (Raspbian) and Free Software Synthesizers (mostly). 4 | 5 | The [ZynthianOS SD-image](https://os.zynthian.org/zynthianos-last-stable.zip) includes all the software you need for building a ZynthianBox, including a good amount of sound libraries and presets. 6 | 7 | This repository contains the zyncoder library. A library for managing zynthian's input/output devices. It supports switch buttons, rotary encoders (PEC11), infinite potentiometers (RV12), analog inputs (ADS1115) and outputs (MCP2748), TOF sensors, etc. 8 | 9 | For compiling the library is required the next packages: 10 | 11 | * liblo (liblo.so) 12 | 13 | For compiling for real use (not emulation), is required: 14 | 15 | * wiringPi (libwiringPi.so) 16 | 17 | For building execute: 18 | ``` 19 | $ ./build.sh 20 | ``` 21 | 22 | You can learn more about the Zynthian Project in any of our sites: 23 | 24 | + [website](https://zynthian.org) 25 | + [wiki](https://wiki.zynthian.org) 26 | + [blog](https://blog.zynthian.org) 27 | + [forum](https://discourse.zynthian.org) => Join the conversation!! 28 | 29 | You can buy official kits in the zynthian shop: 30 | 31 | + [shop](https://shop.zynthian.org) 32 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __all__ = [ 3 | "zyncore" 4 | ] 5 | from zyncoder.zyncore import * 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | 5 | pushd $DIR 6 | if [ ! -d build ]; then 7 | mkdir build 8 | fi 9 | pushd build 10 | 11 | # Detect headphones amplifier kernel driver 12 | if lsmod | grep -wq "^snd_soc_tpa6130a2"; then 13 | export TPA6130_KERNEL_DRIVER_LOADED=1 14 | else 15 | export TPA6130_KERNEL_DRIVER_LOADED=0 16 | fi 17 | 18 | if [ "$1" == "debug" ]; then 19 | cmake -DCMAKE_BUILD_TYPE=Debug .. 20 | else 21 | cmake .. 22 | fi 23 | make 24 | popd 25 | popd 26 | -------------------------------------------------------------------------------- /gpiod_callback.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: libgpiod callbacks 4 | * 5 | * Implements a callback mechanism for libgpiod 6 | * 7 | * Copyright (C) 2015-2024 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "gpiod_callback.h" 34 | 35 | //------------------------------------------------------------------- 36 | // Variables 37 | //------------------------------------------------------------------- 38 | 39 | // GPIO Chip Data Structure 40 | struct gpiod_chip *gpio_chip = NULL; 41 | 42 | // Array of callback structures 43 | struct gpiod_callback rpi_gpiod_callbacks[NUM_GPIO_PINS]; 44 | 45 | // Bulk structure for callback lines 46 | struct gpiod_line_bulk cb_line_bulk; 47 | 48 | int end_callback_thread_flag = 0; 49 | pthread_t callback_thread_tid; 50 | 51 | //------------------------------------------------------------------- 52 | // Pin number conversion arrays 53 | //------------------------------------------------------------------- 54 | 55 | // WiringPi => GPIO number (BCM) 56 | int8_t wpi2gpio[32] = { 57 | 17, // 0 58 | 18, // 1 59 | 27, // 2 60 | 22, // 3 61 | 23, // 4 62 | 24, // 5 63 | 25, // 6 64 | 4, // 7 65 | 2, // 8 66 | 3, // 9 67 | 8, // 10 68 | 7, // 11 69 | 10, // 12 70 | 9, // 13 71 | 11, // 14 72 | 14, // 15 73 | 15, // 16 74 | -1, // 17 75 | -1, // 18 76 | -1, // 19 77 | -1, // 20 78 | 5, // 21 79 | 6, // 22 80 | 13, // 23 81 | 19, // 24 82 | 26, // 25 83 | 12, // 26 84 | 16, // 27 85 | 20, // 28 86 | 21, // 29 87 | 0, // 30 88 | 1 // 31 89 | }; 90 | 91 | // GPIO number (BCM) => WiringPi 92 | int8_t gpio2wpi[28] = { 93 | 30, // 0 94 | 31, // 1 95 | 8, // 2 96 | 9, // 3 97 | 7, // 4 98 | 21, // 5 99 | 22, // 6 100 | 11, // 7 101 | 10, // 8 102 | 13, // 9 103 | 12, // 10 104 | 14, // 11 105 | 26, // 12 106 | 23, // 13 107 | 15, // 14 108 | 16, // 15 109 | 27, // 16 110 | 0, // 17 111 | 1, // 18 112 | 24, // 19 113 | 28, // 20 114 | 29, // 21 115 | 3, // 22 116 | 4, // 23 117 | 5, // 24 118 | 6, // 25 119 | 25, // 26 120 | 2 // 27 121 | }; 122 | 123 | //------------------------------------------------------------------- 124 | // Initialization & functions 125 | //------------------------------------------------------------------- 126 | 127 | int gpiod_init_callbacks() { 128 | int i; 129 | 130 | // Initialize GPIOD callback data structures 131 | for (i=0; igpiod_init_callbacks(): Can't open RPI's GPIO chip: %s\n", gpio_chip_device); 143 | gpio_chip = NULL; 144 | return 0; 145 | } 146 | return 1; 147 | } 148 | 149 | int gpiod_line_register_callback(struct gpiod_line *line, void (*callback)(void)) { 150 | if (line) { 151 | int pin = gpiod_line_offset(line); 152 | rpi_gpiod_callbacks[pin].pin = pin; 153 | rpi_gpiod_callbacks[pin].line = line; 154 | rpi_gpiod_callbacks[pin].callback = callback; 155 | //fprintf(stderr, "ZynCore->gpiod_line_register_callback(): Registered callback on pin %d\n", pin); 156 | return 1; 157 | } 158 | return 0; 159 | } 160 | 161 | int gpiod_line_unregister_callback(struct gpiod_line *line) { 162 | if (line) { 163 | int pin = gpiod_line_offset(line); 164 | rpi_gpiod_callbacks[pin].pin = -1; 165 | rpi_gpiod_callbacks[pin].line = NULL; 166 | rpi_gpiod_callbacks[pin].callback = NULL; 167 | return 1; 168 | } 169 | return 0; 170 | } 171 | 172 | void * gpiod_callbacks_thread(void *arg) { 173 | end_callback_thread_flag = 0; 174 | struct timespec ts = { 1, 0 }; 175 | struct gpiod_line_bulk event_bulk; 176 | struct gpiod_line *line; 177 | struct gpiod_line_event event; 178 | int ret = 0; 179 | int pin; 180 | int i; 181 | while (!end_callback_thread_flag) { 182 | ret = gpiod_line_event_wait_bulk(&cb_line_bulk, &ts, &event_bulk); 183 | if (ret > 0) { 184 | for (i=0; igpiod_callback_thread(): Got event on pin '%d'!\n", pin); 190 | rpi_gpiod_callbacks[pin].callback(); 191 | } 192 | } else if (ret < 0) { 193 | fprintf(stderr, "ZynCore->gpiod_callback_thread(): Error while processing GPIO events!\n"); 194 | break; 195 | } else { 196 | //fprintf(stderr, "ZynCore->gpiod_callback_thread(): Event loop timeout...\n"); 197 | } 198 | } 199 | fprintf(stderr, "ZynCore->gpiod_callback_thread(): Finished succesfully\n"); 200 | } 201 | 202 | int gpiod_start_callbacks() { 203 | int i, count; 204 | gpiod_line_bulk_init(&cb_line_bulk); 205 | for (i=0, count=0; i 0) { 213 | // Start callback thread 214 | int err = pthread_create(&callback_thread_tid, NULL, &gpiod_callbacks_thread, NULL); 215 | if (err != 0) { 216 | fprintf(stderr, "ZynCore->gpiod_start_callbacks: Can't create callback thread :[%s]", strerror(err)); 217 | return 0; 218 | } else { 219 | fprintf(stderr, "ZynCore->gpiod_start_callbacks: Callback thread created successfully\n"); 220 | return 1; 221 | } 222 | } 223 | return 0; 224 | } 225 | 226 | int gpiod_stop_callbacks() { 227 | end_callback_thread_flag = 1; 228 | return 1; 229 | } 230 | 231 | int gpiod_restart_callbacks() { 232 | gpiod_stop_callbacks(); 233 | return gpiod_start_callbacks(); 234 | } 235 | 236 | //------------------------------------------------------------------- -------------------------------------------------------------------------------- /gpiod_callback.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: libgpiod callbacks 4 | * 5 | * Implements a callback mechanism for libgpiod 6 | * 7 | * Copyright (C) 2015-2024 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #ifndef GPIOD_CALLBACK_H 27 | #define GPIOD_CALLBACK_H 28 | 29 | #include 30 | 31 | #define DEFAULT_GPIO_CHIP_DEVICE "/dev/gpiochip0" 32 | #define NUM_GPIO_PINS 28 33 | #define ZYNCORE_CONSUMER "zyncore" 34 | 35 | // Callback data structure 36 | struct gpiod_callback { 37 | int pin; 38 | struct gpiod_line *line; 39 | void (*callback)(void); 40 | }; 41 | 42 | extern struct gpiod_chip *gpio_chip; 43 | 44 | extern int8_t wpi2gpio[32]; 45 | extern int8_t gpio2wpi[28]; 46 | 47 | // ------------------------------------------------------------------- 48 | 49 | int gpiod_init_callbacks(); 50 | int gpiod_line_register_callback(struct gpiod_line *line, void (*callback)(void)); 51 | int gpiod_line_unregister_callback(struct gpiod_line *line); 52 | int gpiod_start_callbacks(); 53 | int gpiod_stop_callbacks(); 54 | int gpiod_restart_callbacks(); 55 | 56 | // ------------------------------------------------------------------- 57 | 58 | #endif -------------------------------------------------------------------------------- /lm4811.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for LM4811 amplifier 4 | * 5 | * Library for interfacing the LM4811 headphones amplifier. 6 | * It implements the volume control using 2 GPIO pins: 7 | * - AMP_VOL => 1=Up / 0=Down 8 | * - AMP_CLK => Volume step control 9 | * 10 | * Copyright (C) 2015-2021 Fernando Moyano 11 | * 12 | * ****************************************************************** 13 | * 14 | * This program is free software; you can redistribute it and/or 15 | * modify it under the terms of the GNU General Public License as 16 | * published by the Free Software Foundation; either version 2 of 17 | * the License, or any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * 24 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 25 | * 26 | * ****************************************************************** 27 | */ 28 | 29 | #include "gpiod.h" 30 | #include "gpiod_callback.h" 31 | #include "lm4811.h" 32 | 33 | //------------------------------------------------------------------- 34 | 35 | //#define DEBUG 36 | 37 | #if Z2_VERSION==1 38 | #define PIN_AMP_CLK 11 // wiringPi 14 39 | #define PIN_AMP_VOL 12 // wiringPi 26 40 | #else 41 | #define PIN_AMP_CLK 4 // wiringPi 7 42 | #define PIN_AMP_VOL 16 // wiringPi 27 43 | #endif 44 | 45 | #define AMP_MAX_VOL 15 46 | #define STEP_USECS 100 47 | 48 | struct gpiod_line *line_clk; 49 | struct gpiod_line *line_vol; 50 | uint8_t current_volume = 0; 51 | 52 | //------------------------------------------------------------------- 53 | 54 | void lm4811_volume_steps(int n) { 55 | if (n==0) return; 56 | #ifdef DEBUG 57 | fprintf(stdout, "Sending %d volume steps to LM4811...\n", n); 58 | #endif 59 | if (n>0) { 60 | gpiod_line_set_value(line_vol, 1); 61 | } else { 62 | gpiod_line_set_value(line_vol, 0); 63 | n = -n; 64 | } 65 | int i; 66 | for (i=0;iAMP_MAX_VOL) vol = AMP_MAX_VOL; 81 | int n_steps = vol - current_volume; 82 | if (n_steps!=0) { 83 | lm4811_volume_steps(n_steps); 84 | current_volume = vol; 85 | } 86 | return current_volume; 87 | } 88 | 89 | uint8_t lm4811_get_volume() { 90 | return current_volume; 91 | } 92 | 93 | uint8_t lm4811_get_volume_max() { 94 | return AMP_MAX_VOL; 95 | } 96 | 97 | int lm4811_init() { 98 | line_clk = gpiod_chip_get_line(gpio_chip, PIN_AMP_CLK); 99 | line_vol = gpiod_chip_get_line(gpio_chip, PIN_AMP_VOL); 100 | if (!line_clk || !line_vol) { 101 | fprintf(stderr, "ZynCore->lm4811_init(): Can't get lines for lm4811\n"); 102 | return 0; 103 | } 104 | if (gpiod_line_request_output(line_clk, ZYNCORE_CONSUMER, 0) < 0) { 105 | fprintf(stderr, "ZynCore->lm4811_init(): Can't request CLK output for lm4811\n"); 106 | return 0; 107 | } 108 | if (gpiod_line_request_output(line_vol, ZYNCORE_CONSUMER, 0) < 0) { 109 | fprintf(stderr, "ZynCore->lm4811_init(): Can't request VOL output for lm4811\n"); 110 | return 0; 111 | } 112 | usleep(STEP_USECS); 113 | lm4811_reset_volume(); 114 | lm4811_set_volume(10); 115 | } 116 | 117 | int lm4811_end() { 118 | lm4811_reset_volume(); 119 | return 1; 120 | } 121 | 122 | //------------------------------------------------------------------- 123 | -------------------------------------------------------------------------------- /lm4811.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for LM4811 amplifier 4 | * 5 | * Library for interfacing the LM4811 headphones amplifier. 6 | * It implements the volume control using 2 GPIO pins: 7 | * - AMP_VOL => 1=Up / 0=Down 8 | * - AMP_CLK => Volume step control 9 | * 10 | * Copyright (C) 2015-2021 Fernando Moyano 11 | * 12 | * ****************************************************************** 13 | * 14 | * This program is free software; you can redistribute it and/or 15 | * modify it under the terms of the GNU General Public License as 16 | * published by the Free Software Foundation; either version 2 of 17 | * the License, or any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * 24 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 25 | * 26 | * ****************************************************************** 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | //------------------------------------------------------------------- 35 | 36 | void lm4811_volume_steps(int n); 37 | void lm4811_reset_volume(); 38 | uint8_t lm4811_set_volume(uint8_t vol); 39 | uint8_t lm4811_get_volume(); 40 | uint8_t lm4811_get_volume_max(); 41 | int lm4811_init(); 42 | int lm4811_end(); 43 | 44 | //------------------------------------------------------------------- 45 | -------------------------------------------------------------------------------- /lm4811_set_volume.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for LM4811 amplifier 4 | * 5 | * Basic CLI for setting volume 6 | * Usage: 7 | * lm4811_set_volume [0-15] 8 | * 9 | * Copyright (C) 2015-2021 Fernando Moyano 10 | * 11 | * ****************************************************************** 12 | * 13 | * This program is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU General Public License as 15 | * published by the Free Software Foundation; either version 2 of 16 | * the License, or any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 24 | * 25 | * ****************************************************************** 26 | */ 27 | 28 | #include "lm4811.h" 29 | 30 | //------------------------------------------------------------------- 31 | 32 | int main(int argc, char *argv[]) { 33 | uint8_t vol = 10; 34 | if (argc>=2) { 35 | vol = atoi(argv[1]); 36 | } 37 | lm4811_init(); 38 | printf("Setting LM4811 volume to %d\n", vol); 39 | lm4811_set_volume(vol); 40 | //lm4811_end(); 41 | } 42 | 43 | //------------------------------------------------------------------- 44 | -------------------------------------------------------------------------------- /mcp4728_set_address.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | typedef uint8_t bool; 5 | 6 | #include 7 | 8 | #define MCP4728_I2C_ADDR 0x60 9 | #define MCP4728_LDAC_GPIO 24 10 | 11 | 12 | int main() { 13 | struct chip * mcp4728_chip = mcp4728_initialize(2, 3, MCP4728_LDAC_GPIO, MCP4728_I2C_ADDR); 14 | mcp4728_setaddress(mcp4728_chip, 0x64); 15 | mcp4728_deinitialize(mcp4728_chip); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tpa6130.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for TPA6130 amplifier 4 | * 5 | * Library for interfacing the TPA6130 headphones amplifier. 6 | * It implements the volume control using I2C 7 | * 8 | * Copyright (C) 2015-2023 Fernando Moyano 9 | * 10 | * ****************************************************************** 11 | * 12 | * This program is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU General Public License as 14 | * published by the Free Software Foundation; either version 2 of 15 | * the License, or any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | * 24 | * ****************************************************************** 25 | */ 26 | 27 | #include "wiringPiI2C.h" 28 | #include "tpa6130.h" 29 | 30 | //------------------------------------------------------------------- 31 | 32 | //#define DEBUG 33 | 34 | #define TPA6130_I2C_ADDRESS 0x60 35 | #define AMP_MAX_VOL 0x3F // 0-63 36 | 37 | int tpa6130_fd = 0x0; 38 | 39 | //------------------------------------------------------------------- 40 | 41 | uint8_t tpa6130_set_volume(uint8_t vol) { 42 | wiringPiI2CWriteReg8(tpa6130_fd, 0x2, AMP_MAX_VOL & vol); 43 | return vol; 44 | } 45 | 46 | uint8_t tpa6130_get_volume() { 47 | return wiringPiI2CReadReg8(tpa6130_fd, 0x2) & 0x3C; 48 | } 49 | 50 | uint8_t tpa6130_get_volume_max() { 51 | return AMP_MAX_VOL; 52 | } 53 | 54 | void tpa6130_init() { 55 | tpa6130_fd = wiringPiI2CSetup(TPA6130_I2C_ADDRESS); 56 | wiringPiI2CWriteReg8(tpa6130_fd, 0x1, 0xC0); 57 | tpa6130_set_volume(20); 58 | } 59 | 60 | void tpa6130_end() { 61 | wiringPiI2CWriteReg8(tpa6130_fd, 0x1, 0x00); 62 | } 63 | 64 | //------------------------------------------------------------------- 65 | -------------------------------------------------------------------------------- /tpa6130.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for TPA6130 amplifier 4 | * 5 | * Library for interfacing the TPA6130 headphones amplifier. 6 | * It implements the volume control using I2C 7 | * 8 | * Copyright (C) 2015-2023 Fernando Moyano 9 | * 10 | * ****************************************************************** 11 | * 12 | * This program is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU General Public License as 14 | * published by the Free Software Foundation; either version 2 of 15 | * the License, or any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | * 24 | * ****************************************************************** 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | //------------------------------------------------------------------- 33 | 34 | uint8_t tpa6130_set_volume(uint8_t vol); 35 | uint8_t tpa6130_get_volume(); 36 | uint8_t tpa6130_get_volume_max(); 37 | void tpa6130_init(); 38 | void tpa6130_end(); 39 | 40 | //------------------------------------------------------------------- 41 | -------------------------------------------------------------------------------- /tpa6130_set_volume.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Headphones Volume Control for TPA6130 amplifier 4 | * 5 | * Basic CLI for setting volume 6 | * Usage: 7 | * tpa6130_set_volume [0-15] 8 | * 9 | * Copyright (C) 2015-2023 Fernando Moyano 10 | * 11 | * ****************************************************************** 12 | * 13 | * This program is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU General Public License as 15 | * published by the Free Software Foundation; either version 2 of 16 | * the License, or any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 24 | * 25 | * ****************************************************************** 26 | */ 27 | 28 | #include "tpa6130.h" 29 | 30 | //------------------------------------------------------------------- 31 | 32 | int main(int argc, char *argv[]) { 33 | uint8_t vol = 10; 34 | if (argc>=2) { 35 | vol = atoi(argv[1]); 36 | } 37 | tpa6130_init(); 38 | printf("Setting TPA6130 volume to %d\n", vol); 39 | tpa6130_set_volume(vol); 40 | //tpa6130_end(); 41 | } 42 | 43 | //------------------------------------------------------------------- 44 | -------------------------------------------------------------------------------- /wiringPiI2C.c: -------------------------------------------------------------------------------- 1 | /* 2 | * wiringPiI2C.c: 3 | * Simplified I2C access routines 4 | * Copyright (c) 2013 Gordon Henderson 5 | *********************************************************************** 6 | * This file is part of wiringPi: 7 | * https://github.com/WiringPi/WiringPi/ 8 | * 9 | * wiringPi is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * wiringPi is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with wiringPi. 21 | * If not, see . 22 | *********************************************************************** 23 | */ 24 | 25 | /* 26 | * Notes: 27 | * The Linux I2C code is actually the same (almost) as the SMBus code. 28 | * SMBus is System Management Bus - and in essentially I2C with some 29 | * additional functionality added, and stricter controls on the electrical 30 | * specifications, etc. however I2C does work well with it and the 31 | * protocols work over both. 32 | * 33 | * I'm directly including the SMBus functions here as some Linux distros 34 | * lack the correct header files, and also some header files are GPLv2 35 | * rather than the LGPL that wiringPi is released under - presumably because 36 | * originally no-one expected I2C/SMBus to be used outside the kernel - 37 | * however enter the Raspberry Pi with people now taking directly to I2C 38 | * devices without going via the kernel... 39 | * 40 | * This may ultimately reduce the flexibility of this code, but it won't be 41 | * hard to maintain it and keep it current, should things change. 42 | * 43 | * Information here gained from: kernel/Documentation/i2c/dev-interface 44 | * as well as other online resources. 45 | ********************************************************************************* 46 | */ 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | //#include "wiringPi.h" 58 | #include "wiringPiI2C.h" 59 | 60 | // I2C definitions 61 | 62 | #define I2C_SLAVE 0x0703 63 | #define I2C_SMBUS 0x0720 /* SMBus-level access */ 64 | 65 | #define I2C_SMBUS_READ 1 66 | #define I2C_SMBUS_WRITE 0 67 | 68 | // SMBus transaction types 69 | 70 | #define I2C_SMBUS_QUICK 0 71 | #define I2C_SMBUS_BYTE 1 72 | #define I2C_SMBUS_BYTE_DATA 2 73 | #define I2C_SMBUS_WORD_DATA 3 74 | #define I2C_SMBUS_PROC_CALL 4 75 | #define I2C_SMBUS_BLOCK_DATA 5 76 | #define I2C_SMBUS_I2C_BLOCK_BROKEN 6 77 | #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ 78 | #define I2C_SMBUS_I2C_BLOCK_DATA 8 79 | 80 | // SMBus messages 81 | 82 | #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ 83 | #define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */ 84 | 85 | // Structures used in the ioctl() calls 86 | 87 | union i2c_smbus_data 88 | { 89 | uint8_t byte ; 90 | uint16_t word ; 91 | uint8_t block [I2C_SMBUS_BLOCK_MAX + 2] ; // block [0] is used for length + one more for PEC 92 | } ; 93 | 94 | struct i2c_smbus_ioctl_data 95 | { 96 | char read_write ; 97 | uint8_t command ; 98 | int size ; 99 | union i2c_smbus_data *data ; 100 | } ; 101 | 102 | static inline int i2c_smbus_access (int fd, char rw, uint8_t command, int size, union i2c_smbus_data *data) 103 | { 104 | struct i2c_smbus_ioctl_data args ; 105 | 106 | args.read_write = rw ; 107 | args.command = command ; 108 | args.size = size ; 109 | args.data = data ; 110 | return ioctl (fd, I2C_SMBUS, &args) ; 111 | } 112 | 113 | /* 114 | * wiringPiI2CRead: 115 | * Simple device read 116 | ********************************************************************************* 117 | */ 118 | 119 | int wiringPiI2CRead (int fd) 120 | { 121 | union i2c_smbus_data data ; 122 | 123 | if (i2c_smbus_access (fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data)) 124 | return -1 ; 125 | else 126 | return data.byte & 0xFF ; 127 | } 128 | 129 | /* 130 | * wiringPiI2CReadReg8: wiringPiI2CReadReg16: 131 | * Read an 8 or 16-bit value from a regsiter on the device 132 | ********************************************************************************* 133 | */ 134 | 135 | int wiringPiI2CReadReg8 (int fd, int reg) 136 | { 137 | union i2c_smbus_data data; 138 | 139 | if (i2c_smbus_access (fd, I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data)) 140 | return -1 ; 141 | else 142 | return data.byte & 0xFF ; 143 | } 144 | 145 | int wiringPiI2CReadReg16 (int fd, int reg) 146 | { 147 | union i2c_smbus_data data; 148 | 149 | if (i2c_smbus_access (fd, I2C_SMBUS_READ, reg, I2C_SMBUS_WORD_DATA, &data)) 150 | return -1 ; 151 | else 152 | return data.word & 0xFFFF ; 153 | } 154 | 155 | /* 156 | * wiringPiI2CWrite: 157 | * Simple device write 158 | ********************************************************************************* 159 | */ 160 | 161 | int wiringPiI2CWrite (int fd, int data) 162 | { 163 | return i2c_smbus_access (fd, I2C_SMBUS_WRITE, data, I2C_SMBUS_BYTE, NULL) ; 164 | } 165 | 166 | /* 167 | * wiringPiI2CWriteReg8: wiringPiI2CWriteReg16: 168 | * Write an 8 or 16-bit value to the given register 169 | ********************************************************************************* 170 | */ 171 | 172 | int wiringPiI2CWriteReg8 (int fd, int reg, int value) 173 | { 174 | union i2c_smbus_data data ; 175 | 176 | data.byte = value ; 177 | return i2c_smbus_access (fd, I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data) ; 178 | } 179 | 180 | int wiringPiI2CWriteReg16 (int fd, int reg, int value) 181 | { 182 | union i2c_smbus_data data ; 183 | 184 | data.word = value ; 185 | return i2c_smbus_access (fd, I2C_SMBUS_WRITE, reg, I2C_SMBUS_WORD_DATA, &data) ; 186 | } 187 | 188 | /* 189 | * wiringPiI2CSetupInterface: 190 | * Undocumented access to set the interface explicitly - might be used 191 | * for the Pi's 2nd I2C interface... 192 | ********************************************************************************* 193 | */ 194 | 195 | int wiringPiI2CSetupInterface (const char *device, int devId) 196 | { 197 | int fd ; 198 | if ((fd = open (device, O_RDWR)) < 0) { 199 | fprintf(stderr, "Unable to open I2C device: %s\n", strerror(errno)); 200 | return 0; 201 | } 202 | if (ioctl (fd, I2C_SLAVE, devId) < 0) { 203 | fprintf(stderr, "Unable to select I2C device: %s\n", strerror(errno)); 204 | return 0; 205 | } 206 | return fd ; 207 | } 208 | 209 | /* 210 | * wiringPiI2CSetup: 211 | * Open the I2C device, and register the target device 212 | ********************************************************************************* 213 | */ 214 | 215 | int wiringPiI2CSetup (const int devId) 216 | { 217 | char * i2c_device = getenv ("I2C_DEVICE"); 218 | if (!i2c_device) i2c_device = DEFAULT_I2C_DEVICE; 219 | return wiringPiI2CSetupInterface (i2c_device, devId) ; 220 | } 221 | -------------------------------------------------------------------------------- /wiringPiI2C.h: -------------------------------------------------------------------------------- 1 | /* 2 | * wiringPiI2C.h: 3 | * Simplified I2C access routines 4 | * Copyright (c) 2013 Gordon Henderson 5 | *********************************************************************** 6 | * This file is part of wiringPi: 7 | * https://github.com/WiringPi/WiringPi/ 8 | * 9 | * wiringPi is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation, either version 3 of the 12 | * License, or (at your option) any later version. 13 | * 14 | * wiringPi is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with wiringPi. 21 | * If not, see . 22 | *********************************************************************** 23 | */ 24 | 25 | #define DEFAULT_I2C_DEVICE "/dev/i2c-1" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | extern int wiringPiI2CRead (int fd) ; 32 | extern int wiringPiI2CReadReg8 (int fd, int reg) ; 33 | extern int wiringPiI2CReadReg16 (int fd, int reg) ; 34 | 35 | extern int wiringPiI2CWrite (int fd, int data) ; 36 | extern int wiringPiI2CWriteReg8 (int fd, int reg, int data) ; 37 | extern int wiringPiI2CWriteReg16 (int fd, int reg, int data) ; 38 | 39 | extern int wiringPiI2CSetupInterface (const char *device, int devId) ; 40 | extern int wiringPiI2CSetup (const int devId) ; 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /zynads1115.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: ads1115 low level access 4 | * 5 | * Implements the low level code to interface ADS1115 I2C 16-bit ADC 6 | * 7 | * Copyright (C) 2021-2024 Fernando Moyano 8 | * Copyright (C) 2016 Gordon Henderson 9 | * This code inherits from the venerable but currently 10 | * unmaintained wiringPi library, by Gordon Henderson 11 | * Thanks for your great work, Gordon! 12 | * 13 | * ****************************************************************** 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License as 17 | * published by the Free Software Foundation; either version 2 of 18 | * the License, or any later version. 19 | * 20 | * This program is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 26 | * 27 | * ****************************************************************** 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "wiringPiI2C.h" 37 | #include "zynads1115.h" 38 | 39 | 40 | // Bits in the config register (it's a 16-bit register) 41 | #define CONFIG_OS_MASK (0x8000) // Operational Status Register 42 | #define CONFIG_OS_SINGLE (0x8000) // Write - Starts a single-conversion 43 | // Read 1 = Conversion complete 44 | 45 | // The multiplexor 46 | #define CONFIG_MUX_MASK (0x7000) 47 | // Differential modes 48 | #define CONFIG_MUX_DIFF_0_1 (0x0000) // Pos = AIN0, Neg = AIN1 (default) 49 | #define CONFIG_MUX_DIFF_0_3 (0x1000) // Pos = AIN0, Neg = AIN3 50 | #define CONFIG_MUX_DIFF_1_3 (0x2000) // Pos = AIN1, Neg = AIN3 51 | #define CONFIG_MUX_DIFF_2_3 (0x3000) // Pos = AIN2, Neg = AIN3 (2nd differential channel) 52 | // Single-ended modes 53 | #define CONFIG_MUX_SINGLE_0 (0x4000) // AIN0 54 | #define CONFIG_MUX_SINGLE_1 (0x5000) // AIN1 55 | #define CONFIG_MUX_SINGLE_2 (0x6000) // AIN2 56 | #define CONFIG_MUX_SINGLE_3 (0x7000) // AIN3 57 | 58 | // Programmable Gain Amplifier 59 | #define CONFIG_PGA_MASK (0x0E00) 60 | #define CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3 61 | #define CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1 62 | #define CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default) 63 | #define CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4 64 | #define CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8 65 | #define CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16 66 | 67 | #define CONFIG_MODE (0x0100) // 0 is continuous, 1 is single-shot (default) 68 | 69 | // Data Rate 70 | #define CONFIG_DR_MASK (0x00E0) 71 | #define CONFIG_DR_8SPS (0x0000) // 8 samples per second 72 | #define CONFIG_DR_16SPS (0x0020) // 16 samples per second 73 | #define CONFIG_DR_32SPS (0x0040) // 32 samples per second 74 | #define CONFIG_DR_64SPS (0x0060) // 64 samples per second 75 | #define CONFIG_DR_128SPS (0x0080) // 128 samples per second (default) 76 | #define CONFIG_DR_250SPS (0x00A0) // 250 samples per second 77 | #define CONFIG_DR_475SPS (0x00C0) // 475 samples per second 78 | #define CONFIG_DR_860SPS (0x00E0) // 860 samples per second 79 | 80 | // Comparator mode 81 | #define CONFIG_CMODE_MASK (0x0010) 82 | #define CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default) 83 | #define CONFIG_CMODE_WINDOW (0x0010) // Window comparator 84 | 85 | // Comparator polarity - the polarity of the output alert/rdy pin 86 | #define CONFIG_CPOL_MASK (0x0008) 87 | #define CONFIG_CPOL_ACTVLOW (0x0000) // Active low (default) 88 | #define CONFIG_CPOL_ACTVHI (0x0008) // Active high 89 | 90 | // Latching comparator - does the alert/rdy pin latch 91 | #define CONFIG_CLAT_MASK (0x0004) 92 | #define CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default) 93 | #define CONFIG_CLAT_LATCH (0x0004) // Latching comparator 94 | 95 | // Comparator queue 96 | #define CONFIG_CQUE_MASK (0x0003) 97 | #define CONFIG_CQUE_1CONV (0x0000) // Assert after one conversions 98 | #define CONFIG_CQUE_2CONV (0x0001) // Assert after two conversions 99 | #define CONFIG_CQUE_4CONV (0x0002) // Assert after four conversions 100 | #define CONFIG_CQUE_NONE (0x0003) // Disable the comparator (default) 101 | 102 | #define CONFIG_DEFAULT (0x8583) // From the datasheet 103 | 104 | 105 | static const uint16_t dataRates [8] = 106 | { 107 | CONFIG_DR_8SPS, CONFIG_DR_16SPS, CONFIG_DR_32SPS, CONFIG_DR_64SPS, CONFIG_DR_128SPS, CONFIG_DR_250SPS, CONFIG_DR_475SPS, CONFIG_DR_860SPS 108 | } ; 109 | 110 | static const uint16_t dataGains [6] = 111 | { 112 | CONFIG_PGA_6_144V, CONFIG_PGA_4_096V, CONFIG_PGA_2_048V, CONFIG_PGA_1_024V, CONFIG_PGA_0_512V, CONFIG_PGA_0_256V 113 | } ; 114 | 115 | 116 | //----------------------------------------------------------------------------- 117 | 118 | /* 119 | * ads115_config: 120 | * Calculate the base content of the config register 121 | * Calculate the time (us) to wait for the conversion to complete 122 | ********************************************************************************* 123 | */ 124 | 125 | int ads1115_config (ads1115_t *ads1115) 126 | { 127 | ads1115->base_config = CONFIG_DEFAULT ; 128 | 129 | // Set PGA/voltage range 130 | ads1115->base_config &= ~CONFIG_PGA_MASK ; 131 | ads1115->base_config |= ads1115->gain ; 132 | 133 | // Set sample speed 134 | ads1115->base_config &= ~CONFIG_DR_MASK ; 135 | ads1115->base_config |= ads1115->rate ; 136 | 137 | // Calculate the time (us) to wait for the conversion to complete 138 | uint32_t dus ; 139 | switch (ads1115->rate >> 5) 140 | { 141 | case 0: dus = 1000000 / 8; break ; 142 | case 1: dus = 1000000 / 16; break ; 143 | case 2: dus = 1000000 / 32; break ; 144 | case 3: dus = 1000000 / 64; break ; 145 | case 4: dus = 1000000 / 128 ;break ; 146 | case 5: dus = 1000000 / 250 ;break ; 147 | case 6: dus = 1000000 / 475 ;break ; 148 | case 7: dus = 1000000 / 860 ;break ; 149 | default: dus = 10000; break ; 150 | } 151 | dus = 20 + 11 * dus / 10 ; 152 | ads1115->read_wait_us = dus ; 153 | 154 | //fprintf(stderr, "ADS1115 on address 0x%x => config = 0x%x, wait = %d\n", ads1115->i2c_address, ads1115->base_config, ads1115->read_wait_us); 155 | 156 | return 1 ; 157 | } 158 | 159 | /* 160 | * init_ads1115: 161 | * Initialize an ads1115 IC using the I2C interface. 162 | * 163 | * i2c_address : I2C address 164 | * uint8_t gain : Initial gain 165 | * uint8_t rate : Initial sample rate 166 | * Returns 1 on success, 0 on fail 167 | ********************************************************************************* 168 | */ 169 | int init_ads1115(ads1115_t *ads1115, uint16_t i2c_address, uint8_t gain, uint8_t rate) { 170 | int fd = wiringPiI2CSetup(i2c_address); 171 | if (fd < 0) return 0 ; 172 | 173 | // Use default if out of range 174 | if (gain > 5) gain = ADS1115_GAIN_VREF_2_048 ; 175 | if (rate > 7) rate = ADS1115_RATE_128SPS ; 176 | 177 | ads1115->i2c_address = i2c_address; 178 | ads1115->fd = fd ; 179 | ads1115->gain = dataGains[gain] ; // Gain 180 | ads1115->rate = dataRates[rate] ; // Samples/sec 181 | return ads1115_config (ads1115) ; 182 | } 183 | 184 | /* 185 | * set_ads1115_gain: 186 | * gain : gain index 187 | ********************************************************************************* 188 | */ 189 | void ads1115_set_gain(ads1115_t *ads1115, uint8_t gain) { 190 | if (gain > 5) gain = ADS1115_GAIN_VREF_2_048 ; // Use default if out of range 191 | ads1115->gain = dataGains [gain] ; 192 | ads1115_config (ads1115) ; 193 | } 194 | 195 | /* 196 | * set_ads1115_rate: 197 | * rate : rate index 198 | ********************************************************************************* 199 | */ 200 | void ads1115_set_rate(ads1115_t *ads1115, uint8_t rate) { 201 | if (rate > 7) rate = ADS1115_RATE_128SPS ; // Use default if out of range 202 | ads1115->rate = dataRates [rate] ; 203 | ads1115_config (ads1115) ; 204 | } 205 | 206 | /* 207 | * ads1115_set_comparator_threshold: 208 | * chan : channel to configure 209 | * data : data to write to the 2 comparator threshold registers. 210 | * 211 | ********************************************************************************* 212 | */ 213 | void ads1115_set_comparator_threshold (ads1115_t *ads1115, uint8_t chan, int16_t data) { 214 | chan &= 3 ; 215 | int reg = chan + 2 ; 216 | data = __bswap_16 (data) ; 217 | wiringPiI2CWriteReg16 (ads1115->fd, reg, data) ; 218 | } 219 | 220 | /* 221 | * ads1115_analog_read 222 | * channels 0-3 are single ended inputs (pin voltage) 223 | * channels 4-7 are the various differential combinations. 224 | ********************************************************************************* 225 | */ 226 | int16_t ads1115_analog_read(ads1115_t *ads1115, uint8_t chan) { 227 | int16_t result; 228 | 229 | chan &= 3 ; 230 | 231 | // Setup the configuration register 232 | uint16_t config = ads1115->base_config ; 233 | 234 | // Set single-ended channel or differential mode 235 | config &= ~CONFIG_MUX_MASK ; 236 | switch (chan) { 237 | case 0: config |= CONFIG_MUX_SINGLE_0 ; break ; 238 | case 1: config |= CONFIG_MUX_SINGLE_1 ; break ; 239 | case 2: config |= CONFIG_MUX_SINGLE_2 ; break ; 240 | case 3: config |= CONFIG_MUX_SINGLE_3 ; break ; 241 | 242 | case 4: config |= CONFIG_MUX_DIFF_0_1 ; break ; 243 | case 5: config |= CONFIG_MUX_DIFF_2_3 ; break ; 244 | case 6: config |= CONFIG_MUX_DIFF_0_3 ; break ; 245 | case 7: config |= CONFIG_MUX_DIFF_1_3 ; break ; 246 | } 247 | 248 | // Start a single conversion 249 | config |= CONFIG_OS_SINGLE ; 250 | 251 | config = __bswap_16 (config) ; 252 | wiringPiI2CWriteReg16 (ads1115->fd, 1, config) ; 253 | 254 | // Wait for the conversion to complete 255 | int i = 0; 256 | for (;;) { 257 | delay_microseconds (ads1115->read_wait_us) ; 258 | result = wiringPiI2CReadReg16 (ads1115->fd, 1) ; 259 | result = __bswap_16 (result) ; 260 | if ((result & CONFIG_OS_MASK) != 0) break ; 261 | else if (i++ > 10) { 262 | fprintf(stderr, "ZynCore->ads1115_analog_read(0x%x, %d): TimeOut with status 0x%x!!\n", ads1115->i2c_address, chan, result); 263 | return 0; 264 | } 265 | } 266 | 267 | // Read the result 268 | result = wiringPiI2CReadReg16 (ads1115->fd, 0) ; 269 | result = __bswap_16 (result) ; 270 | 271 | // Sometimes with a 0v input on a single-ended channel the internal 0v reference 272 | // can be higher than the input, so you get a negative result... 273 | if ( (chan < 4) && (result < 0) ) return 0 ; 274 | else return result ; 275 | } 276 | 277 | //----------------------------------------------------------------------------- 278 | 279 | /* 280 | * delayMicroseconds: 281 | * This is somewhat interesting. It seems that on the Pi, a single call 282 | * to nanosleep takes some 80 to 130 microseconds anyway, so while 283 | * obeying the standards (may take longer), it's not always what we 284 | * want! 285 | * 286 | * So what I'll do now is if the delay is less than 100uS we'll do it 287 | * in a hard loop, watching a built-in counter on the ARM chip. This is 288 | * somewhat sub-optimal in that it uses 100% CPU, something not an issue 289 | * in a microcontroller, but under a multi-tasking, multi-user OS, it's 290 | * wastefull, however we've no real choice )-: 291 | * 292 | * Plan B: It seems all might not be well with that plan, so changing it 293 | * to use gettimeofday () and poll on that instead... 294 | ********************************************************************************* 295 | */ 296 | 297 | void delay_microseconds_hard (unsigned int howLong) 298 | { 299 | struct timeval tNow, tLong, tEnd ; 300 | gettimeofday (&tNow, NULL) ; 301 | tLong.tv_sec = howLong / 1000000 ; 302 | tLong.tv_usec = howLong % 1000000 ; 303 | timeradd (&tNow, &tLong, &tEnd) ; 304 | 305 | while (timercmp (&tNow, &tEnd, <)) 306 | gettimeofday (&tNow, NULL) ; 307 | } 308 | 309 | void delay_microseconds (unsigned int howLong) 310 | { 311 | if (howLong == 0) 312 | return ; 313 | else if (howLong < 100) 314 | delay_microseconds_hard (howLong) ; 315 | else 316 | { 317 | struct timespec sleeper ; 318 | unsigned int uSecs = howLong % 1000000 ; 319 | unsigned int wSecs = howLong / 1000000 ; 320 | sleeper.tv_sec = wSecs ; 321 | sleeper.tv_nsec = (long)(uSecs * 1000L) ; 322 | nanosleep (&sleeper, NULL) ; 323 | } 324 | } 325 | 326 | //----------------------------------------------------------------------------- 327 | -------------------------------------------------------------------------------- /zynads1115.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: ads1115 low level access 4 | * 5 | * Implements the low level code to interface ADS1115 I2C 16-bit ADC 6 | * 7 | * Copyright (C) 2021-2024 Fernando Moyano 8 | * Copyright (C) 2016 Gordon Henderson 9 | * This code inherits from the venerable but currently 10 | * unmaintained wiringPi library, by Gordon Henderson 11 | * Thanks for your great work, Gordon! 12 | * 13 | * ****************************************************************** 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License as 17 | * published by the Free Software Foundation; either version 2 of 18 | * the License, or any later version. 19 | * 20 | * This program is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 26 | * 27 | * ****************************************************************** 28 | */ 29 | 30 | #ifndef ZYNADS1115_H 31 | #define ZYNADS1115_H 32 | 33 | #include 34 | 35 | //----------------------------------------------------------------------------- 36 | 37 | 38 | // Gain 39 | #define ADS1115_GAIN_6 0 40 | #define ADS1115_GAIN_4 1 41 | #define ADS1115_GAIN_2 2 42 | #define ADS1115_GAIN_1 3 43 | #define ADS1115_GAIN_HALF 4 44 | #define ADS1115_GAIN_QUARTER 5 45 | 46 | #define ADS1115_GAIN_VREF_6_144 0 47 | #define ADS1115_GAIN_VREF_4_096 1 48 | #define ADS1115_GAIN_VREF_2_048 2 49 | #define ADS1115_GAIN_VREF_1_024 3 50 | #define ADS1115_GAIN_VREF_0_512 4 51 | #define ADS1115_GAIN_VREF_0_256 5 52 | 53 | // Data rate 54 | #define ADS1115_DR_8 0 55 | #define ADS1115_DR_16 1 56 | #define ADS1115_DR_32 2 57 | #define ADS1115_DR_64 3 58 | #define ADS1115_DR_128 4 59 | #define ADS1115_DR_250 5 60 | #define ADS1115_DR_475 6 61 | #define ADS1115_DR_860 7 62 | 63 | #define ADS1115_RATE_8SPS 0 64 | #define ADS1115_RATE_16SPS 1 65 | #define ADS1115_RATE_32SPS 2 66 | #define ADS1115_RATE_64SPS 3 67 | #define ADS1115_RATE_128SPS 4 68 | #define ADS1115_RATE_250SPS 5 69 | #define ADS1115_RATE_475SPS 6 70 | #define ADS1115_RATE_860SPS 7 71 | 72 | #ifdef __cplusplus 73 | extern "C" { 74 | #endif 75 | 76 | //----------------------------------------------------------------------------- 77 | 78 | typedef struct ads1115_st { 79 | uint16_t i2c_address; 80 | int fd; 81 | uint16_t gain; 82 | uint16_t rate; 83 | uint16_t base_config; 84 | uint32_t read_wait_us; 85 | } ads1115_t; 86 | 87 | //----------------------------------------------------------------------------- 88 | 89 | int init_ads1115(ads1115_t *ads1115, uint16_t i2c_address, uint8_t gain, uint8_t rate); 90 | void ads1115_set_gain(ads1115_t *ads1115, uint8_t gain); 91 | void ads1115_set_rate(ads1115_t *ads1115, uint8_t rate); 92 | void ads1115_set_comparator_threshold (ads1115_t *ads1115, uint8_t chan, int16_t data); 93 | int16_t ads1115_analog_read(ads1115_t *ads1115, uint8_t chan); 94 | void delay_microseconds (unsigned int howLong); 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif 99 | 100 | //----------------------------------------------------------------------------- 101 | 102 | #endif -------------------------------------------------------------------------------- /zynaptik.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zynaptik Library 4 | * 5 | * Library for interfacing external sensors and actuators. 6 | * It implements interfaces with extra MCP23017, ADS1115, etc. 7 | * 8 | * Copyright (C) 2015-2019 Fernando Moyano 9 | * 10 | * ****************************************************************** 11 | * 12 | * This program is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU General Public License as 14 | * published by the Free Software Foundation; either version 2 of 15 | * the License, or any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | * 24 | * ****************************************************************** 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "zynpot.h" 38 | #include "zyncoder.h" 39 | #include "zynaptik.h" 40 | 41 | //----------------------------------------------------------------------------- 42 | // Global variables 43 | //----------------------------------------------------------------------------- 44 | 45 | extern zynswitch_t zynswitches[MAX_NUM_ZYNSWITCHES]; 46 | 47 | struct zyncvin_st zyncvins[MAX_NUM_ZYNCVINS]; 48 | struct zyncvout_st zyncvouts[MAX_NUM_ZYNCVOUTS]; 49 | struct zyngateout_st zyngateouts[MAX_NUM_ZYNGATEOUTS]; 50 | 51 | float k_cvin; 52 | int note0_cvin; 53 | float k_cvout; 54 | int note0_cvout; 55 | pthread_mutex_t zynaptik_cvin_lock; 56 | 57 | //----------------------------------------------------------------------------- 58 | // MCP23017 Stuff 59 | //----------------------------------------------------------------------------- 60 | 61 | int zynaptik_mcp23017_index; 62 | 63 | // two ISR routines for the two banks 64 | void zynaptik_mcp23017_bankA_ISR() { 65 | zynmcp23017_ISR(zynaptik_mcp23017_index, 0); 66 | } 67 | void zynaptik_mcp23017_bankB_ISR() { 68 | zynmcp23017_ISR(zynaptik_mcp23017_index, 1); 69 | } 70 | void (*zynaptik_mcp23017_bank_ISRs[2])={ 71 | zynaptik_mcp23017_bankA_ISR, 72 | zynaptik_mcp23017_bankB_ISR 73 | }; 74 | 75 | //----------------------------------------------------------------------------- 76 | // ADS1115 Stuff 77 | //----------------------------------------------------------------------------- 78 | 79 | ads1115_t zynaptik_ads1115_node; 80 | 81 | //----------------------------------------------------------------------------- 82 | // MCP4728 Stuff 83 | //----------------------------------------------------------------------------- 84 | 85 | void * mcp4728_chip; 86 | 87 | //----------------------------------------------------------------------------- 88 | // CV-IN: Generate MIDI from Analog Inputs: CC, Pitchbend, Channel Pressure 89 | //----------------------------------------------------------------------------- 90 | 91 | void setup_zynaptik_cvin(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num) { 92 | zyncvins[i].midi_evt = midi_evt; 93 | zyncvins[i].midi_chan = midi_chan & 0xF; 94 | zyncvins[i].midi_num = midi_num & 0x7F; 95 | zyncvins[i].midi_val = 0; 96 | zyncvins[i].enabled = 1; 97 | } 98 | 99 | void zynaptik_disable_cvin(uint8_t i) { 100 | zyncvins[i].enabled = 0; 101 | } 102 | 103 | void zynaptik_cvin_set_volts_octave(float vo) { 104 | k_cvin = K_CVIN_VOLT_OCTAVE / vo; 105 | } 106 | 107 | float zynaptik_cvin_get_volts_octave() { 108 | return K_CVIN_VOLT_OCTAVE / k_cvin; 109 | } 110 | 111 | void zynaptik_cvin_set_note0(int note0) { 112 | note0_cvin = note0; 113 | } 114 | 115 | int zynaptik_cvin_get_note0() { 116 | return note0_cvin; 117 | } 118 | 119 | int32_t zynaptik_get_cvin(uint8_t ch) { 120 | return ads1115_analog_read(&zynaptik_ads1115_node, ch); 121 | } 122 | 123 | void zynaptik_cvin_to_midi(uint8_t i, uint16_t val) { 124 | if (zyncvins[i].midi_evt==PITCH_BEND) { 125 | val>>=1; 126 | //Send MIDI event to engines and ouput (ZMOPS) 127 | zmip_send_pitchbend_change(ZMIP_FAKE_INT, zyncvins[i].midi_chan, val); 128 | zyncvins[i].midi_val=val; 129 | return; 130 | } 131 | val>>=8; 132 | if (val==zyncvins[i].midi_val) return; 133 | //fprintf(stderr, "ZYNAPTIK CV-IN [%d] => MIDI event %d, %d, %d\n", i, zyncvins[i].midi_evt, zyncvins[i].midi_num, val); 134 | if (zyncvins[i].midi_evt==CTRL_CHANGE) { 135 | //Send MIDI event to engines and output (ZMOPS) 136 | zmip_send_ccontrol_change(ZMIP_FAKE_INT, zyncvins[i].midi_chan, zyncvins[i].midi_num, val); 137 | //Send MIDI event to UI 138 | write_zynmidi_ccontrol_change(zyncvins[i].midi_chan, zyncvins[i].midi_num, val); 139 | } 140 | else if (zyncvins[i].midi_evt==CHAN_PRESS) { 141 | //Send MIDI event to engines and ouput (ZMOPS) 142 | zmip_send_chan_press(ZMIP_FAKE_INT, zyncvins[i].midi_chan, val); 143 | } 144 | zyncvins[i].midi_val = val; 145 | } 146 | 147 | void * zynaptik_poll_cvins(void *arg) { 148 | int i; 149 | int32_t val; 150 | while (1) { 151 | for (i=0;i32767) val=32767; 158 | else if (val<0) val=0; 159 | //fprintf(stderr, "ZYNAPTIK CV-IN [%d] => %d\n", i, val); 160 | zynaptik_cvin_to_midi(i,(uint16_t)val); 161 | } 162 | } 163 | usleep(POLL_ZYNAPTIK_CVINS_US); 164 | } 165 | return NULL; 166 | } 167 | 168 | pthread_t zynaptik_init_poll_cvins() { 169 | if (pthread_mutex_init(&zynaptik_cvin_lock, NULL) != 0) { 170 | fprintf(stderr,"ZynCore: Zynaptik CV-IN mutex init failed\n"); 171 | return 0; 172 | } 173 | pthread_t tid; 174 | int err=pthread_create(&tid, NULL, &zynaptik_poll_cvins, NULL); 175 | if (err != 0) { 176 | fprintf(stderr,"ZynCore: Can't create zynaptik CV-IN poll thread :[%s]", strerror(err)); 177 | return 0; 178 | } else { 179 | fprintf(stderr, "ZynCore: Zynaptik CV-IN poll thread created successfully\n"); 180 | return tid; 181 | } 182 | } 183 | 184 | //----------------------------------------------------------------------------- 185 | // CV-OUT: Set Analog Outputs from MIDI: CC, Pitchbend, Channel Pressure, Notes (velocity+pitchbend) 186 | //----------------------------------------------------------------------------- 187 | 188 | void zynaptik_setup_cvout(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num) { 189 | if (midi_evt==CVGATE_OUT_EVENT) { 190 | zyncvouts[i].midi_event_mask=0xEF00; 191 | zyncvouts[i].midi_event_temp=((NOTE_OFF&0xF)<<12) | ((midi_chan&0xF)<<8); 192 | } 193 | else if (midi_evt==PITCH_BEND || midi_evt==CHAN_PRESS) { 194 | zyncvouts[i].midi_event_mask=0xFF00; 195 | zyncvouts[i].midi_event_temp=((midi_evt&0xF)<<12) | ((midi_chan&0xF)<<8); 196 | } 197 | else if (midi_evt==CTRL_CHANGE) { 198 | zyncvouts[i].midi_event_mask=0xFF7F; 199 | zyncvouts[i].midi_event_temp=((midi_evt&0xF)<<12) | ((midi_chan&0xF)<<8) | (midi_num&0x7F); 200 | } 201 | else { 202 | return; 203 | } 204 | zyncvouts[i].midi_evt = midi_evt; 205 | zyncvouts[i].midi_chan = midi_chan & 0xF; 206 | zyncvouts[i].midi_num = midi_num & 0x7F; 207 | for (int j=0; j<128; ++j) zyncvouts[i].note[j] = 0; 208 | zyncvouts[i].val = 0; 209 | zyncvouts[i].enabled = 1; 210 | } 211 | 212 | void zynaptik_disable_cvout(uint8_t i) { 213 | zyncvouts[i].val = 0; 214 | zyncvouts[i].enabled = 0; 215 | } 216 | 217 | void zynaptik_cvout_set_volts_octave(float vo) { k_cvout = K_CVOUT_VOLT_OCTAVE / vo; } 218 | float zynaptik_cvout_get_volts_octave() { return K_CVOUT_VOLT_OCTAVE / k_cvout; } 219 | void zynaptik_cvout_set_note0(int note0) { note0_cvout = note0; } 220 | int zynaptik_cvout_get_note0() { return note0_cvout; } 221 | 222 | void zynaptik_midi_to_cvout(jack_midi_event_t *ev) { 223 | uint8_t event_type = ev->buffer[0] >> 4; 224 | if (event_typePITCH_BEND) return; 225 | //fprintf(stderr, "ZYNAPTIK MIDI TO CV-OUT => [0x%x, %d, %d]\n", ev->buffer[0], ev->buffer[1], ev->buffer[2]); 226 | 227 | uint16_t ev_data = ev->buffer[0]<<8 | ev->buffer[1]; 228 | zynswitch_t *zsw; 229 | for (int i=0;i 0x%x <=> 0x%x\n", i, ev_data & zyncvouts[i].midi_event_mask, zyncvouts[i].midi_event_temp); 232 | if (zyncvouts[i].midi_event_temp!=(ev_data & zyncvouts[i].midi_event_mask)) continue; 233 | 234 | if (event_type==NOTE_ON && ev->buffer[2]>0) { 235 | //fprintf(stderr, "ZYNAPTIK MIDI TO CVGATE-OUT %d NOTE-ON => %d, %d\n", zyncvouts[i].midi_num, ev->buffer[1], ev->buffer[2]); 236 | zsw = &zynswitches[zyncvouts[i].midi_num]; 237 | if (zsw->status!=zsw->off_state) { 238 | write_pin_zynmcp23017(zsw->pin, zsw->off_state); 239 | zsw->status = zsw->off_state; 240 | } 241 | zyncvouts[i].val = (int)(((ev->buffer[1]-note0_cvout)<<7)/k_cvout); 242 | //set_zynaptik_cvout(i, zyncvouts[i].val); 243 | zynaptik_refresh_cvouts(); 244 | // Wait until CV-out is set before triggering the gate 245 | usleep(20); 246 | write_pin_zynmcp23017(zsw->pin, ~zsw->off_state); 247 | zsw->status = ~zsw->off_state; 248 | } 249 | else if (event_type==NOTE_OFF || event_type==NOTE_ON) { 250 | //fprintf(stderr, "ZYNAPTIK MIDI TO CVGATE-OUT %d NOTE-OFF => %d\n", zyncvouts[i].midi_num, ev->buffer[1]); 251 | zsw = &zynswitches[zyncvouts[i].midi_num]; 252 | write_pin_zynmcp23017(zsw->pin, zsw->off_state); 253 | zsw->status = zsw->off_state; 254 | } 255 | else if (event_type==PITCH_BEND) { 256 | zyncvouts[i].val = (ev->buffer[2]<<7) | ev->buffer[1]; 257 | //set_zynaptik_cvout(i, zyncvouts[i].val); 258 | zynaptik_refresh_cvouts(); 259 | } 260 | else if (event_type==CTRL_CHANGE) { 261 | zyncvouts[i].val = ev->buffer[2]<<7; 262 | //set_zynaptik_cvout(i, zyncvouts[i].val); 263 | zynaptik_refresh_cvouts(); 264 | } 265 | else if (event_type==CHAN_PRESS) { 266 | zyncvouts[i].val = ev->buffer[2]<<7; 267 | //set_zynaptik_cvout(i, zyncvouts[i].val); 268 | zynaptik_refresh_cvouts(); 269 | } 270 | } 271 | } 272 | 273 | void zynaptik_set_cvout(int i, uint16_t val) { 274 | float vout=val/16384.0; 275 | //fprintf(stderr, "ZYNAPTIK CV-OUT %d => %f\n", i, vout); 276 | int err=mcp4728_singleexternal(mcp4728_chip, i, vout, 0); 277 | if (err!=0) { 278 | fprintf(stderr,"ZYNAPTIK CV-OUT => Can't write MCP4728 (DAC) register %d. ERROR %d\n", i, err); 279 | } 280 | } 281 | 282 | void zynaptik_refresh_cvouts() { 283 | int i, err; 284 | float buffer[MAX_NUM_ZYNCVOUTS]; 285 | for (i=0;i [%f, %f, %f, %f]\n", buffer[0], buffer[1], buffer[2], buffer[3]); 293 | //err=mcp4728_multipleinternal(mcp4728_chip, buffer, 0); 294 | err=mcp4728_multipleexternal(mcp4728_chip, buffer, 0); 295 | if (err!=0) { 296 | fprintf(stderr,"ZYNAPTIK CV-OUT => Can't write MCP4728 (DAC) registers. ERROR %d\n", err); 297 | } 298 | } 299 | 300 | /* 301 | void * _zynaptik_refresh_cvouts(void *arg) { 302 | while (1) { 303 | refresh_zynaptik_cvouts(); 304 | usleep(REFRESH_ZYNAPTIK_CVOUTS_US); 305 | } 306 | return NULL; 307 | } 308 | 309 | pthread_t zynaptik_init_refresh_cvouts() { 310 | pthread_t tid; 311 | int err=pthread_create(&tid, NULL, &_refresh_zynaptik_cvouts, NULL); 312 | if (err != 0) { 313 | fprintf(stderr,"Zyncoder: Can't create zynaptik CV-OUT refresh thread :[%s]", strerror(err)); 314 | return 0; 315 | } else { 316 | fprintf(stderr, "Zyncoder: Zynaptik CV-OUT refresh thread created successfully.\n"); 317 | return tid; 318 | } 319 | } 320 | */ 321 | 322 | //----------------------------------------------------------------------------- 323 | // GATE-OUT: Set Digital Outputs from MIDI Notes 324 | //----------------------------------------------------------------------------- 325 | 326 | void zynaptik_setup_gateout(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num) { 327 | if (midi_evt==GATE_OUT_EVENT) { 328 | zyngateouts[i].midi_event_mask=0xEF7F; 329 | zyngateouts[i].midi_event_temp=((NOTE_OFF&0xF)<<12) | ((midi_chan&0xF)<<8) | (midi_num&0x7F); 330 | } 331 | else { 332 | return; 333 | } 334 | zyngateouts[i].midi_evt = midi_evt; 335 | zyngateouts[i].midi_chan = midi_chan & 0xF; 336 | zyngateouts[i].midi_num = midi_num & 0x7F; 337 | zyngateouts[i].enabled = 1; 338 | } 339 | 340 | void zynaptik_disable_gateout(uint8_t i) { 341 | zyngateouts[i].enabled = 0; 342 | } 343 | 344 | void zynaptik_midi_to_gateout(jack_midi_event_t *ev) { 345 | uint8_t event_type = ev->buffer[0] >> 4; 346 | if (event_typeNOTE_ON) return; 347 | //fprintf(stderr, "ZYNAPTIK MIDI TO GATE-OUT => [0x%x, %d, %d]\n", ev->buffer[0], ev->buffer[1], ev->buffer[2]); 348 | 349 | uint16_t ev_data = ev->buffer[0]<<8 | ev->buffer[1]; 350 | for (int i=0;i 0x%x <=> 0x%x\n", i, ev_data & zyngateouts[i].midi_event_mask, zyngateouts[i].midi_event_temp); 353 | if (zyngateouts[i].midi_event_temp!=(ev_data & zyngateouts[i].midi_event_mask)) continue; 354 | 355 | if (event_type==NOTE_ON && ev->buffer[2]>0) { 356 | zynswitch_t *zsw = &zynswitches[i]; 357 | //fprintf(stderr, "ZYNAPTIK MIDI TO GATE-OUT %d NOTE-ON => %d, %d\n", i, ev->buffer[1], ev->buffer[2]); 358 | write_pin_zynmcp23017(zsw->pin, ~zsw->off_state); 359 | zsw->status = ~zsw->off_state; 360 | } 361 | else if (event_type==NOTE_OFF || event_type==NOTE_ON) { 362 | zynswitch_t *zsw = &zynswitches[i]; 363 | //fprintf(stderr, "ZYNAPTIK MIDI TO GATE-OUT %d NOTE-OFF => %d, 0\n", i, ev->buffer[1]); 364 | write_pin_zynmcp23017(zsw->pin, zsw->off_state); 365 | zsw->status = zsw->off_state; 366 | } 367 | } 368 | } 369 | 370 | void zynaptik_all_gates_off() { 371 | int i; 372 | zynswitch_t *zsw; 373 | for (i=0;ipin, zsw->off_state); 377 | zsw->status = zsw->off_state; 378 | } 379 | for (i=0;ipin, zsw->off_state); 383 | zsw->status = zsw->off_state; 384 | } 385 | } 386 | 387 | //----------------------------------------------------------------------------- 388 | // Zynaptik Library Initialization 389 | //----------------------------------------------------------------------------- 390 | 391 | extern uint16_t num_zynswitches; 392 | 393 | int init_zynaptik() { 394 | int i; 395 | for (i=0;i 9 | * 10 | * ****************************************************************** 11 | * 12 | * This program is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU General Public License as 14 | * published by the Free Software Foundation; either version 2 of 15 | * the License, or any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | * 24 | * ****************************************************************** 25 | */ 26 | 27 | #ifndef ZYNNAPTIK_H 28 | #define ZYNANPTIK_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "zynads1115.h" 38 | #include 39 | 40 | //----------------------------------------------------------------------------- 41 | // MCP23017 Stuff 42 | //----------------------------------------------------------------------------- 43 | 44 | //Default config for Zynaptik's MCP23017 45 | #if !defined(ZYNAPTIK_MCP23017_I2C_ADDRESS) 46 | #define ZYNAPTIK_MCP23017_I2C_ADDRESS 0x21 47 | #endif 48 | #if !defined(ZYNAPTIK_MCP23017_BASE_PIN) 49 | #define ZYNAPTIK_MCP23017_BASE_PIN 200 50 | #endif 51 | #if !defined(ZYNAPTIK_MCP23017_INTA_PIN) 52 | #define ZYNAPTIK_MCP23017_INTA_PIN 27 53 | #endif 54 | #if !defined(ZYNAPTIK_MCP23017_INTB_PIN) 55 | #define ZYNAPTIK_MCP23017_INTB_PIN 25 56 | #endif 57 | 58 | //----------------------------------------------------------------------------- 59 | // ADS1115 Stuff 60 | //----------------------------------------------------------------------------- 61 | 62 | //Default config for Zynaptik's ADS1115 63 | #if !defined(ZYNAPTIK_ADS1115_I2C_ADDRESS) 64 | #define ZYNAPTIK_ADS1115_I2C_ADDRESS 0x48 65 | #endif 66 | #if !defined(ZYNAPTIK_ADS1115_BASE_PIN) 67 | #define ZYNAPTIK_ADS1115_BASE_PIN 300 68 | #endif 69 | 70 | //----------------------------------------------------------------------------- 71 | // MCP4728 Stuff 72 | //----------------------------------------------------------------------------- 73 | 74 | //Default config for Zynaptik's MCP4728 75 | #if !defined(ZYNAPTIK_MCP4728_I2C_ADDRESS) 76 | #if ZYNAPTIK_VERSION==1 77 | #define ZYNAPTIK_MCP4728_I2C_ADDRESS 0x60 78 | #else 79 | #define ZYNAPTIK_MCP4728_I2C_ADDRESS 0x61 80 | #endif 81 | #endif 82 | 83 | //----------------------------------------------------------------------------- 84 | // CV-IN: Generate MIDI from Analog Inputs: CC, Pitchbend, Channel Pressure 85 | //----------------------------------------------------------------------------- 86 | 87 | #define MAX_NUM_ZYNCVINS 4 88 | #define K_CVIN_VOLT_OCTAVE (12.0 * 6.144 / 32767.0) 89 | 90 | #if !defined(ZYNAPTIK_CVIN_VOLTS_OCTAVE) 91 | #define ZYNAPTIK_CVIN_VOLTS_OCTAVE 1.0 92 | #endif 93 | #if !defined(ZYNAPTIK_CVIN_NOTE0) 94 | #define ZYNAPTIK_CVIN_NOTE0 0 95 | #endif 96 | 97 | struct zyncvin_st { 98 | uint8_t enabled; 99 | 100 | int midi_evt; 101 | uint8_t midi_chan; 102 | uint8_t midi_num; 103 | uint16_t midi_val; 104 | }; 105 | 106 | void zynaptik_cvin_set_volts_octave(float vo); 107 | float zynaptik_cvin_get_volts_octave(); 108 | void zynaptik_cvin_set_note0(int note0); 109 | int zynaptik_cvin_get_note0(); 110 | 111 | void zynaptik_setup_cvin(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num); 112 | void zynaptik_disable_cvin(uint8_t i); 113 | 114 | int32_t zynaptik_get_cvin(uint8_t i); 115 | void zynaptik_cvin_to_midi(uint8_t i, uint16_t val); 116 | 117 | //CV-IN Polling interval 118 | #define POLL_ZYNAPTIK_CVINS_US 40000 119 | pthread_t init_poll_zynaptik_cvins(); 120 | 121 | //----------------------------------------------------------------------------- 122 | // CV-OUT: Set Analog Outputs from MIDI: CC, Notes (velocity+pitchbend) 123 | //----------------------------------------------------------------------------- 124 | 125 | #define MAX_NUM_ZYNCVOUTS 4 126 | #define K_CVOUT_VOLT_OCTAVE (60.0 / (127.0 * 0.97)) 127 | 128 | #if !defined(ZYNAPTIK_CVOUT_VOLTS_OCTAVE) 129 | #define ZYNAPTIK_CVOUT_VOLTS_OCTAVE 1.0 130 | #endif 131 | #if !defined(ZYNAPTIK_CVOUT_NOTE0) 132 | #define ZYNAPTIK_CVOUT_NOTE0 0 133 | #endif 134 | 135 | struct zyncvout_st { 136 | uint8_t enabled; 137 | 138 | int midi_evt; 139 | uint8_t midi_chan; 140 | uint8_t midi_num; 141 | uint8_t note[128]; 142 | 143 | uint16_t midi_event_temp; 144 | uint16_t midi_event_mask; 145 | 146 | uint16_t val; 147 | }; 148 | 149 | void zynaptik_cvout_set_volts_octave(float vo); 150 | float zynaptik_cvout_get_volts_octave(); 151 | void zynaptik_cvout_set_note0(int note0); 152 | int zynaptik_cvout_get_note0(); 153 | 154 | void zynaptik_setup_cvout(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num); 155 | void zynaptik_disable_cvout(uint8_t i); 156 | void zynaptik_midi_to_cvout(jack_midi_event_t *ev); 157 | void zynaptik_set_cvout(int i, uint16_t val); 158 | void zynaptik_refresh_cvouts(); 159 | 160 | //CV-OUT Refresh interval 161 | #define REFRESH_ZYNAPTIK_CVOUTS_US 40000 162 | 163 | //----------------------------------------------------------------------------- 164 | // GATE-OUT: Set Digital Outputs from MIDI Notes 165 | //----------------------------------------------------------------------------- 166 | 167 | #define MAX_NUM_ZYNGATEOUTS 36 168 | 169 | struct zyngateout_st { 170 | uint8_t enabled; 171 | 172 | int midi_evt; 173 | uint8_t midi_chan; 174 | uint8_t midi_num; 175 | 176 | uint16_t midi_event_temp; 177 | uint16_t midi_event_mask; 178 | }; 179 | 180 | void zynaptik_setup_gateout(uint8_t i, int midi_evt, uint8_t midi_chan, uint8_t midi_num); 181 | void zynaptik_disable_gateout(uint8_t i); 182 | void zynaptik_midi_to_gateout(jack_midi_event_t *ev); 183 | 184 | //----------------------------------------------------------------------------- 185 | // Zynaptik Library Initialization 186 | //----------------------------------------------------------------------------- 187 | 188 | int init_zynaptik(); 189 | int end_zynaptik(); 190 | 191 | //----------------------------------------------------------------------------- 192 | 193 | #endif -------------------------------------------------------------------------------- /zyncoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library 4 | * 5 | * Library for interfacing Rotary Encoders & Switches connected 6 | * to RBPi native GPIOs or expanded with MCP23008/MCP23017. 7 | * Includes an emulator mode for developing on desktop computers. 8 | * 9 | * Copyright (C) 2015-2021 Fernando Moyano 10 | * 11 | * ****************************************************************** 12 | * 13 | * This program is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU General Public License as 15 | * published by the Free Software Foundation; either version 2 of 16 | * the License, or any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 24 | * 25 | * ****************************************************************** 26 | */ 27 | 28 | #ifndef ZYNCODER_H 29 | #define ZYNCODER_H 30 | 31 | #include 32 | 33 | #include "zynmcp23017.h" 34 | #include "zynmcp23008.h" 35 | #include "zynmidirouter.h" 36 | 37 | //----------------------------------------------------------------------------- 38 | // Zynswitch data & functions 39 | //----------------------------------------------------------------------------- 40 | 41 | #if defined(ZYNAPTIK_CONFIG) 42 | #define MAX_NUM_ZYNSWITCHES 52 43 | #else 44 | #define MAX_NUM_ZYNSWITCHES 36 45 | #endif 46 | 47 | typedef struct zynswitch_st { 48 | uint8_t enabled; 49 | 50 | struct gpiod_line *line; // libgpiod line struct 51 | uint16_t pin; 52 | uint8_t off_state; 53 | uint8_t push; 54 | uint64_t tsus; 55 | unsigned int dtus; 56 | uint8_t status; 57 | 58 | midi_event_t midi_event; 59 | int last_cvgate_note; 60 | } zynswitch_t; 61 | 62 | void reset_zynswitches(); 63 | int get_num_zynswitches(); 64 | int get_last_zynswitch_index(); 65 | 66 | int setup_zynswitch(uint8_t i, uint16_t pin, uint8_t off_state); 67 | int setup_zynswitch_midi(uint8_t i, midi_event_type midi_evt, uint8_t midi_chan, uint8_t midi_num, uint8_t midi_val); 68 | 69 | unsigned int get_zynswitch(uint8_t i, unsigned int long_dtus); 70 | int get_next_pending_zynswitch(uint8_t i); 71 | 72 | void send_zynswitch_midi(zynswitch_t *zsw); 73 | void update_zynswitch(uint8_t i, uint8_t status); 74 | 75 | //----------------------------------------------------------------------------- 76 | // Zyncoder data (Incremental Rotary Encoders) 77 | //----------------------------------------------------------------------------- 78 | 79 | #define MAX_NUM_ZYNCODERS 4 80 | 81 | typedef struct zyncoder_st { 82 | uint8_t enabled; // 1 to enable encoder 83 | int32_t step; // Size of change in value for each detent of encoder 84 | int32_t value; // Current encdoder value 85 | int32_t subvalue; // Current encdoder subvalue => inter-detent value 86 | int8_t zpot_i; // Zynpot index assigned to this encoder 87 | 88 | // Next fields are zyncoder-specific 89 | struct gpiod_line *line_a; // libgpiod line struct 90 | struct gpiod_line *line_b; // libgpiod line struct 91 | uint16_t pin_a; // Data GPI 92 | uint16_t pin_b; // Clock GPI 93 | uint8_t short_history; // Quadrant encoder algorithm last two valid states (4 bits) 94 | uint8_t long_history; // Quadrant encoder algorithm last four valid states (8 bits) 95 | 96 | uint64_t tsms; // Absolute time of last encoder change in nanoseconds 97 | uint64_t tsms_detent; // Absolute time of last encoder detent in nanoseconds 98 | } zyncoder_t; 99 | 100 | //----------------------------------------------------------------------------- 101 | // Zyncoder's zynpot API 102 | //----------------------------------------------------------------------------- 103 | 104 | void reset_zyncoders(); 105 | int get_num_zyncoders(); 106 | 107 | int setup_zyncoder(uint8_t i, uint16_t pin_a, uint16_t pin_b); 108 | 109 | int setup_behaviour_zyncoder(uint8_t i, int32_t step); 110 | int32_t get_value_zyncoder(uint8_t i); 111 | 112 | //----------------------------------------------------------------------------- 113 | // Zyncoder's specific functions 114 | //----------------------------------------------------------------------------- 115 | 116 | void update_zyncoder(uint8_t i, uint8_t msb, uint8_t lsb); 117 | 118 | //----------------------------------------------------------------------------- 119 | 120 | #endif -------------------------------------------------------------------------------- /zyncoder_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library Test 4 | * 5 | * Test switches & encoders for zynthian kit configurations 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "gpiod_callback.h" 32 | #include "zynpot.h" 33 | #include "zyncoder.h" 34 | #include "zynaptik.h" 35 | #include "zyncontrol.h" 36 | 37 | //----------------------------------------------------------------------------- 38 | // Callback function 39 | //----------------------------------------------------------------------------- 40 | 41 | void test_zynpot_cb(int8_t i, int32_t val) { 42 | printf("PT-%d = %d\n", i, val); 43 | } 44 | 45 | //----------------------------------------------------------------------------- 46 | // Main function 47 | //----------------------------------------------------------------------------- 48 | 49 | int main() { 50 | int i; 51 | 52 | printf("Starting ZynCore...\n"); 53 | init_zyncontrol(); 54 | init_zynmidirouter(); 55 | 56 | #ifdef DEBUG 57 | if (zynpots[0].type==ZYNPOT_RV112) { 58 | fprintf(stdout, "Range 25 = %d\n", RV112_ADS1115_RANGE_25); 59 | fprintf(stdout, "Range 50 = %d\n", RV112_ADS1115_RANGE_50); 60 | fprintf(stdout, "Range 75 = %d\n", RV112_ADS1115_RANGE_75); 61 | fprintf(stdout, "Range 100 = %d\n", RV112_ADS1115_RANGE_100); 62 | } 63 | #endif 64 | 65 | int last_zynswitch_index = get_last_zynswitch_index(); 66 | int num_zynswitches = get_num_zynswitches(); 67 | int num_zynpots = get_num_zynpots(); 68 | 69 | //Configure zynpots 70 | setup_zynpot_cb(test_zynpot_cb); 71 | for (i=0; i0) fprintf(stdout, "SW-%d = %d\n", i, dtus); 81 | i++; 82 | } 83 | /* 84 | for (i=0;i 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | //----------------------------------------------------------------------------- 27 | 28 | int init_zyncontrol(); 29 | int end_zyncontrol(); 30 | 31 | //----------------------------------------------------------------------------- 32 | -------------------------------------------------------------------------------- /zyncontrol_mini_v2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncontrol Library for Zynthian Kit MINI V2 4 | * 5 | * Initialize & configure control hardware for Zynthian Kit MINI V2 6 | * 7 | * Copyright (C) 2015-2023 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include "gpiod_callback.h" 30 | #include "zynpot.h" 31 | #include "zyncoder.h" 32 | 33 | 34 | //----------------------------------------------------------------------------- 35 | // GPIO Expander 1 36 | //----------------------------------------------------------------------------- 37 | 38 | #define MCP23017_1_BASE_PIN 100 39 | #define MCP23017_1_I2C_ADDRESS 0x20 40 | #define MCP23017_1_INTA_PIN 5 // wiringPi 21 41 | #define MCP23017_1_INTB_PIN 6 // wiringPi 22 42 | 43 | void zynmcp23017_ISR_bankA_1() { 44 | zynmcp23017_ISR(0, 0); 45 | } 46 | void zynmcp23017_ISR_bankB_1() { 47 | zynmcp23017_ISR(0, 1); 48 | } 49 | void (*zynmcp23017_ISRs_1[2]) = { 50 | zynmcp23017_ISR_bankA_1, 51 | zynmcp23017_ISR_bankB_1 52 | }; 53 | 54 | //----------------------------------------------------------------------------- 55 | // GPIO Expander 2 56 | //----------------------------------------------------------------------------- 57 | 58 | #define MCP23017_2_BASE_PIN 200 59 | #define MCP23017_2_I2C_ADDRESS 0x21 60 | #define MCP23017_2_INTA_PIN 17 // wiringPi 0 61 | #define MCP23017_2_INTB_PIN 27 // wiringPi 2 62 | 63 | void zynmcp23017_ISR_bankA_2() { 64 | zynmcp23017_ISR(1, 0); 65 | } 66 | void zynmcp23017_ISR_bankB_2() { 67 | zynmcp23017_ISR(1, 1); 68 | } 69 | void (*zynmcp23017_ISRs_2[2]) = { 70 | zynmcp23017_ISR_bankA_2, 71 | zynmcp23017_ISR_bankB_2 72 | }; 73 | 74 | //----------------------------------------------------------------------------- 75 | // 2 x zynmcp23017 76 | //----------------------------------------------------------------------------- 77 | 78 | void init_zynmcp23017s() { 79 | reset_zynmcp23017s(); 80 | setup_zynmcp23017(0, MCP23017_1_BASE_PIN, MCP23017_1_I2C_ADDRESS, MCP23017_1_INTA_PIN, MCP23017_1_INTB_PIN, zynmcp23017_ISRs_1); 81 | setup_zynmcp23017(1, MCP23017_2_BASE_PIN, MCP23017_2_I2C_ADDRESS, MCP23017_2_INTA_PIN, MCP23017_2_INTB_PIN, zynmcp23017_ISRs_2); 82 | } 83 | 84 | //----------------------------------------------------------------------------- 85 | // 30 x ZynSwitches (16 on MCP23017_1, 8 on MCP23017_2) 86 | //----------------------------------------------------------------------------- 87 | 88 | extern uint16_t num_zynswitches; 89 | 90 | void init_zynswitches() { 91 | reset_zynswitches(); 92 | int i; 93 | fprintf(stderr, "ZynCore: Setting-up 20+4 x Zynswitches...\n"); 94 | for (i=0;i<16;i++) setup_zynswitch(4+i, MCP23017_1_BASE_PIN + i, 1); 95 | for (i=0;i<8;i++) setup_zynswitch(20+i, MCP23017_2_BASE_PIN + i, 1); 96 | num_zynswitches = 28; 97 | } 98 | 99 | //----------------------------------------------------------------------------- 100 | // 4 x Zynpòts (Analog Encoder RV112) 101 | //----------------------------------------------------------------------------- 102 | 103 | void init_zynpots() { 104 | reset_zyncoders(); 105 | reset_zynpots(); 106 | 107 | fprintf(stderr, "ZynCore: Setting-up Zynpots => 4 x PEC11 ...\n"); 108 | setup_zyncoder(0, MCP23017_2_BASE_PIN + 9, MCP23017_2_BASE_PIN + 8); 109 | setup_zyncoder(1, MCP23017_2_BASE_PIN + 11, MCP23017_2_BASE_PIN + 10); 110 | setup_zyncoder(2, MCP23017_2_BASE_PIN + 13, MCP23017_2_BASE_PIN + 12); 111 | setup_zyncoder(3, MCP23017_2_BASE_PIN + 15, MCP23017_2_BASE_PIN + 14); 112 | int i; 113 | for (i=0;i<4;i++) { 114 | setup_zynpot(i,ZYNPOT_ZYNCODER,i); 115 | } 116 | } 117 | 118 | void end_zynpots() { 119 | reset_zynpots(); 120 | } 121 | 122 | //----------------------------------------------------------------------------- 123 | // Zyncontrol Initialization 124 | //----------------------------------------------------------------------------- 125 | 126 | int init_zyncontrol() { 127 | gpiod_init_callbacks(); 128 | init_zynmcp23017s(); 129 | init_zynswitches(); 130 | init_zynpots(); 131 | gpiod_start_callbacks(); 132 | return 1; 133 | } 134 | 135 | int end_zyncontrol() { 136 | gpiod_stop_callbacks(); 137 | end_zynpots(); 138 | reset_zyncoders(); 139 | reset_zynswitches(); 140 | reset_zynmcp23017s(); 141 | return 1; 142 | } 143 | 144 | //----------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /zyncontrol_v5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncontrol Library for Zynthian Kit V5 4 | * 5 | * Initialize & configure control hardware for Zynthian Kit V5 6 | * 7 | * Copyright (C) 2015-2023 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include "gpiod_callback.h" 30 | #include "zynpot.h" 31 | #include "zyncoder.h" 32 | #include "tpa6130.h" 33 | 34 | //----------------------------------------------------------------------------- 35 | // Zynface V5 36 | //----------------------------------------------------------------------------- 37 | 38 | #ifdef ZYNAPTIK_CONFIG 39 | #include "zynaptik.h" 40 | #else 41 | #define RBPI_GPIO_EXT1 24 // wiringPi 4 42 | #define RBPI_GPIO_EXT2 23 // wiringPi 5 43 | #endif 44 | 45 | //----------------------------------------------------------------------------- 46 | // GPIO Expander 1 47 | //----------------------------------------------------------------------------- 48 | 49 | #define MCP23017_1_BASE_PIN 100 50 | #define MCP23017_1_I2C_ADDRESS 0x20 51 | #define MCP23017_1_INTA_PIN 5 // wiringPi 21 52 | #define MCP23017_1_INTB_PIN 6 // wiringPi 22 53 | 54 | void zynmcp23017_ISR_bankA_1() { 55 | zynmcp23017_ISR(0, 0); 56 | } 57 | void zynmcp23017_ISR_bankB_1() { 58 | zynmcp23017_ISR(0, 1); 59 | } 60 | void (*zynmcp23017_ISRs_1[2]) = { 61 | zynmcp23017_ISR_bankA_1, 62 | zynmcp23017_ISR_bankB_1 63 | }; 64 | 65 | //----------------------------------------------------------------------------- 66 | // GPIO Expander 2 67 | //----------------------------------------------------------------------------- 68 | 69 | #define MCP23017_2_BASE_PIN 200 70 | #define MCP23017_2_I2C_ADDRESS 0x21 71 | #define MCP23017_2_INTA_PIN 17 // wiringPi 0 72 | #define MCP23017_2_INTB_PIN 27 // wiringPi 2 73 | 74 | void zynmcp23017_ISR_bankA_2() { 75 | zynmcp23017_ISR(1, 0); 76 | } 77 | void zynmcp23017_ISR_bankB_2() { 78 | zynmcp23017_ISR(1, 1); 79 | } 80 | void (*zynmcp23017_ISRs_2[2]) = { 81 | zynmcp23017_ISR_bankA_2, 82 | zynmcp23017_ISR_bankB_2 83 | }; 84 | 85 | //----------------------------------------------------------------------------- 86 | // 2 x zynmcp23017 87 | //----------------------------------------------------------------------------- 88 | 89 | void init_zynmcp23017s() { 90 | reset_zynmcp23017s(); 91 | setup_zynmcp23017(0, MCP23017_1_BASE_PIN, MCP23017_1_I2C_ADDRESS, MCP23017_1_INTA_PIN, MCP23017_1_INTB_PIN, zynmcp23017_ISRs_1); 92 | setup_zynmcp23017(1, MCP23017_2_BASE_PIN, MCP23017_2_I2C_ADDRESS, MCP23017_2_INTA_PIN, MCP23017_2_INTB_PIN, zynmcp23017_ISRs_2); 93 | } 94 | 95 | //----------------------------------------------------------------------------- 96 | // 30 x ZynSwitches (16 on MCP23017_1, 8 on MCP23017_2) 97 | //----------------------------------------------------------------------------- 98 | 99 | extern uint16_t num_zynswitches; 100 | 101 | void init_zynswitches() { 102 | reset_zynswitches(); 103 | int i; 104 | fprintf(stderr, "ZynCore: Setting-up 20+4 x Zynswitches...\n"); 105 | for (i=0;i<16;i++) setup_zynswitch(4+i, MCP23017_1_BASE_PIN + i, 1); 106 | for (i=0;i<8;i++) setup_zynswitch(20+i, MCP23017_2_BASE_PIN + i, 1); 107 | num_zynswitches = 28; 108 | #ifndef ZYNAPTIK_CONFIG 109 | fprintf(stderr, "ZynCore: Setting-up 2 x Zynswitches in RBPi GPIO...\n"); 110 | setup_zynswitch(num_zynswitches, RBPI_GPIO_EXT1, 1); 111 | setup_zynswitch(num_zynswitches + 1, RBPI_GPIO_EXT2, 1); 112 | num_zynswitches += 2; 113 | #endif 114 | } 115 | 116 | //----------------------------------------------------------------------------- 117 | // 4 x Zynpòts (Analog Encoder RV112) 118 | //----------------------------------------------------------------------------- 119 | 120 | void init_zynpots() { 121 | reset_zyncoders(); 122 | reset_zynpots(); 123 | 124 | fprintf(stderr, "ZynCore: Setting-up Zynpots => 4 x PEC11 ...\n"); 125 | setup_zyncoder(0, MCP23017_2_BASE_PIN + 8, MCP23017_2_BASE_PIN + 9); 126 | setup_zyncoder(1, MCP23017_2_BASE_PIN + 10, MCP23017_2_BASE_PIN + 11); 127 | setup_zyncoder(2, MCP23017_2_BASE_PIN + 12, MCP23017_2_BASE_PIN + 13); 128 | setup_zyncoder(3, MCP23017_2_BASE_PIN + 14, MCP23017_2_BASE_PIN + 15); 129 | int i; 130 | for (i=0;i<4;i++) { 131 | setup_zynpot(i,ZYNPOT_ZYNCODER,i); 132 | } 133 | } 134 | 135 | void end_zynpots() { 136 | reset_zynpots(); 137 | } 138 | 139 | //----------------------------------------------------------------------------- 140 | // Zyncontrol Initialization 141 | //----------------------------------------------------------------------------- 142 | 143 | #ifdef TPA6130_DRIVER 144 | uint8_t set_hpvol(uint8_t vol) { return tpa6130_set_volume(vol); } 145 | uint8_t get_hpvol() { return tpa6130_get_volume(); } 146 | uint8_t get_hpvol_max() { return tpa6130_get_volume_max(); } 147 | #endif 148 | 149 | int init_zyncontrol() { 150 | gpiod_init_callbacks(); 151 | #ifdef TPA6130_DRIVER 152 | tpa6130_init(); 153 | #endif 154 | init_zynmcp23017s(); 155 | init_zynswitches(); 156 | init_zynpots(); 157 | #ifdef ZYNAPTIK_CONFIG 158 | init_zynaptik(); 159 | #endif 160 | gpiod_start_callbacks(); 161 | return 1; 162 | } 163 | 164 | int end_zyncontrol() { 165 | gpiod_stop_callbacks(); 166 | #ifdef ZYNAPTIK_CONFIG 167 | end_zynaptik(); 168 | #endif 169 | end_zynpots(); 170 | reset_zyncoders(); 171 | reset_zynswitches(); 172 | reset_zynmcp23017s(); 173 | #ifdef TPA6130_DRIVER 174 | tpa6130_end(); 175 | #endif 176 | return 1; 177 | } 178 | 179 | //----------------------------------------------------------------------------- 180 | -------------------------------------------------------------------------------- /zyncontrol_vx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncontrol Library for Zynthian Kits V1-V4 4 | * 5 | * Initialize & configure control hardware for Zynthian Kits V1-V4 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "gpiod_callback.h" 31 | #include "zynmcp23008.h" 32 | #include "zynpot.h" 33 | #include "zyncoder.h" 34 | 35 | #ifdef ZYNAPTIK_CONFIG 36 | #include "zynaptik.h" 37 | #endif 38 | 39 | #ifdef ZYNTOF_CONFIG 40 | #include "zyntof.h" 41 | #endif 42 | 43 | //----------------------------------------------------------------------------- 44 | // GPIO Expander 45 | //----------------------------------------------------------------------------- 46 | 47 | #if defined(MCP23017_ENCODERS) 48 | // pins 100-115 are located on the MCP23017 49 | #define MCP23017_BASE_PIN 100 50 | // default I2C Address for MCP23017 51 | #if !defined(MCP23017_I2C_ADDRESS) 52 | #define MCP23017_I2C_ADDRESS 0x20 53 | #endif 54 | // default interrupt pins for the MCP23017 55 | #if !defined(MCP23017_INTA_PIN) 56 | #define MCP23017_INTA_PIN 27 57 | #endif 58 | #if !defined(MCP23017_INTB_PIN) 59 | #define MCP23017_INTB_PIN 25 60 | #endif 61 | #elif defined(MCP23008_ENCODERS) 62 | // pins 100-107 are located on the MCP23008 63 | #define MCP23008_BASE_PIN 100 64 | #define MCP23008_I2C_ADDRESS 0x20 65 | #endif 66 | 67 | 68 | #if defined(MCP23017_ENCODERS) 69 | 70 | // two ISR routines for the two banks 71 | void zynmcp23017_ISR_bankA() { 72 | zynmcp23017_ISR(0, 0); 73 | } 74 | void zynmcp23017_ISR_bankB() { 75 | zynmcp23017_ISR(0, 1); 76 | } 77 | void (*zynmcp23017_ISRs[2]) = { 78 | zynmcp23017_ISR_bankA, 79 | zynmcp23017_ISR_bankB 80 | }; 81 | 82 | void init_zynmcp23017s() { 83 | reset_zynmcp23017s(); 84 | setup_zynmcp23017(0, MCP23017_BASE_PIN, MCP23017_I2C_ADDRESS, wpi2gpio[MCP23017_INTA_PIN], wpi2gpio[MCP23017_INTB_PIN], zynmcp23017_ISRs); 85 | } 86 | 87 | #endif 88 | 89 | 90 | #if defined(MCP23008_ENCODERS) 91 | 92 | void init_zynmcp23008s() { 93 | reset_zynmcp23008s(); 94 | setup_zynmcp23008(0, MCP23008_BASE_PIN, MCP23008_I2C_ADDRESS); 95 | } 96 | 97 | #endif 98 | 99 | 100 | //----------------------------------------------------------------------------- 101 | // Get wiring config from environment 102 | //----------------------------------------------------------------------------- 103 | 104 | #define NUM_ZYNSWITCHES 36 105 | #define NUM_ZYNPOTS 4 106 | 107 | int16_t zynswitch_pins[NUM_ZYNSWITCHES]; 108 | int16_t zyncoder_pins_a[NUM_ZYNPOTS]; 109 | int16_t zyncoder_pins_b[NUM_ZYNPOTS]; 110 | 111 | extern uint16_t num_zynswitches; 112 | 113 | void reset_wiring_config() { 114 | int16_t i; 115 | for (i=0;i= 0 && res < 100) res = wpi2gpio[res]; 137 | result[i++] = res; 138 | token = strtok_r(NULL, ",", &save_ptr); 139 | } 140 | } 141 | return i; 142 | } 143 | 144 | void get_wiring_config() { 145 | reset_wiring_config(); 146 | num_zynswitches = parse_envar2intarr("ZYNTHIAN_WIRING_SWITCHES", zynswitch_pins, NUM_ZYNSWITCHES); 147 | fprintf(stderr, "ZynCore: Configured %d x Logical Zynswitches...\n", num_zynswitches); 148 | parse_envar2intarr("ZYNTHIAN_WIRING_ENCODER_A", zyncoder_pins_a, NUM_ZYNPOTS); 149 | parse_envar2intarr("ZYNTHIAN_WIRING_ENCODER_B", zyncoder_pins_b, NUM_ZYNPOTS); 150 | } 151 | 152 | //----------------------------------------------------------------------------- 153 | // 8 x ZynSwitches 154 | //----------------------------------------------------------------------------- 155 | 156 | void init_zynswitches() { 157 | reset_zynswitches(); 158 | int16_t i, count; 159 | for (i=0, count=0; i= 0) { 161 | //fprintf(stderr, "ZynCore: Setting-up zynswitch in pin %d...\n", zynswitch_pins[i]); 162 | setup_zynswitch(i, zynswitch_pins[i], 1); 163 | count++; 164 | } 165 | } 166 | fprintf(stderr, "ZynCore: Setting-up %d x Physical Zynswitches...\n", count); 167 | } 168 | 169 | //----------------------------------------------------------------------------- 170 | // 4 x Zynpots (zyncoder => Incremental encoder) 171 | //----------------------------------------------------------------------------- 172 | 173 | void init_zynpots() { 174 | reset_zynpots(); 175 | reset_zyncoders(); 176 | int16_t i, count; 177 | for (i=0, count=0; i= 0 && zyncoder_pins_b[i] >= 0) { 179 | //fprintf(stderr, "ZynCore: Setting-up zyncoder in pins (%d, %d)...\n", zyncoder_pins_a[i], zyncoder_pins_b[i]); 180 | setup_zyncoder(i, zyncoder_pins_a[i], zyncoder_pins_b[i]); 181 | setup_zynpot(i, ZYNPOT_ZYNCODER, i); 182 | count++; 183 | } 184 | } 185 | fprintf(stderr, "ZynCore: Setting-up %d x Zynpots (zyncoders)...\n", count); 186 | } 187 | 188 | //----------------------------------------------------------------------------- 189 | // Zyncontrol Initialization 190 | //----------------------------------------------------------------------------- 191 | 192 | int init_zyncontrol() { 193 | gpiod_init_callbacks(); 194 | get_wiring_config(); 195 | #if defined(MCP23017_ENCODERS) 196 | init_zynmcp23017s(); 197 | #endif 198 | #if defined(MCP23008_ENCODERS) 199 | init_zynmcp23008s(); 200 | #endif 201 | init_zynswitches(); 202 | init_zynpots(); 203 | #ifdef ZYNAPTIK_CONFIG 204 | init_zynaptik(); 205 | #endif 206 | #ifdef ZYNTOF_CONFIG 207 | init_zyntof(); 208 | #endif 209 | gpiod_start_callbacks(); 210 | #if defined(MCP23008_ENCODERS) 211 | init_poll_zynswitches(); 212 | #endif 213 | return 1; 214 | } 215 | 216 | int end_zyncontrol() { 217 | #if defined(MCP23008_ENCODERS) 218 | end_poll_zynswitches(); 219 | #endif 220 | gpiod_stop_callbacks(); 221 | #ifdef ZYNTOF_CONFIG 222 | end_zyntof(); 223 | #endif 224 | #ifdef ZYNAPTIK_CONFIG 225 | end_zynaptik(); 226 | #endif 227 | reset_zynpots(); 228 | reset_zyncoders(); 229 | reset_zynswitches(); 230 | #if defined(MCP23017_ENCODERS) 231 | reset_zynmcp23017s(); 232 | #endif 233 | return 1; 234 | } 235 | 236 | //----------------------------------------------------------------------------- 237 | -------------------------------------------------------------------------------- /zyncontrol_z2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncontrol Library for Zynthian Kits V1-V4 4 | * 5 | * Initialize & configure control hardware for Zynthian Kits V1-V4 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include "gpiod_callback.h" 30 | #include "zynpot.h" 31 | #include "zyncoder.h" 32 | #include "zynads1115.h" 33 | #include "zynrv112.h" 34 | #include "lm4811.h" 35 | 36 | //----------------------------------------------------------------------------- 37 | // GPIO Expander 1 38 | //----------------------------------------------------------------------------- 39 | 40 | #define MCP23017_1_BASE_PIN 100 41 | #define MCP23017_1_I2C_ADDRESS 0x20 42 | #define MCP23017_1_INTA_PIN 5 43 | #define MCP23017_1_INTB_PIN 6 44 | 45 | void zynmcp23017_ISR_bankA_1() { 46 | zynmcp23017_ISR(0, 0); 47 | } 48 | void zynmcp23017_ISR_bankB_1() { 49 | zynmcp23017_ISR(0, 1); 50 | } 51 | void (*zynmcp23017_ISRs_1[2]) = { 52 | zynmcp23017_ISR_bankA_1, 53 | zynmcp23017_ISR_bankB_1 54 | }; 55 | 56 | //----------------------------------------------------------------------------- 57 | // GPIO Expander 2 58 | //----------------------------------------------------------------------------- 59 | 60 | #define MCP23017_2_BASE_PIN 200 61 | #define MCP23017_2_I2C_ADDRESS 0x21 62 | 63 | #if Z2_VERSION==1 64 | #define MCP23017_2_INTA_PIN 7 65 | #define MCP23017_2_INTB_PIN 8 66 | #else 67 | #define MCP23017_2_INTA_PIN 17 68 | #define MCP23017_2_INTB_PIN 27 69 | #endif 70 | 71 | void zynmcp23017_ISR_bankA_2() { 72 | zynmcp23017_ISR(1, 0); 73 | } 74 | void zynmcp23017_ISR_bankB_2() { 75 | zynmcp23017_ISR(1, 1); 76 | } 77 | void (*zynmcp23017_ISRs_2[2]) = { 78 | zynmcp23017_ISR_bankA_2, 79 | zynmcp23017_ISR_bankB_2 80 | }; 81 | 82 | //----------------------------------------------------------------------------- 83 | // 2 x zynmcp23017 84 | //----------------------------------------------------------------------------- 85 | 86 | void init_zynmcp23017s() { 87 | reset_zynmcp23017s(); 88 | setup_zynmcp23017(0, MCP23017_1_BASE_PIN, MCP23017_1_I2C_ADDRESS, MCP23017_1_INTA_PIN, MCP23017_1_INTB_PIN, zynmcp23017_ISRs_1); 89 | setup_zynmcp23017(1, MCP23017_2_BASE_PIN, MCP23017_2_I2C_ADDRESS, MCP23017_2_INTA_PIN, MCP23017_2_INTB_PIN, zynmcp23017_ISRs_2); 90 | } 91 | 92 | //----------------------------------------------------------------------------- 93 | // 30 x ZynSwitches (16 on MCP23017_1, 14 on MCP23017_2) 94 | //----------------------------------------------------------------------------- 95 | 96 | extern uint16_t num_zynswitches; 97 | 98 | void init_zynswitches() { 99 | reset_zynswitches(); 100 | int i; 101 | fprintf(stderr, "ZynCore: Setting-up 30 x Zynswitches...\n"); 102 | for (i = 0; i < 16; i++) setup_zynswitch(4 + i, MCP23017_1_BASE_PIN + i, 1); 103 | for (i = 0; i < 14; i++) setup_zynswitch(20 + i, MCP23017_2_BASE_PIN + i, 1); 104 | num_zynswitches = 34; 105 | } 106 | 107 | //----------------------------------------------------------------------------- 108 | // 4 x Zynpòts (Analog Encoder RV112) 109 | //----------------------------------------------------------------------------- 110 | 111 | #define RV112_ADS1115_I2C_ADDRESS_1 0x48 112 | #define RV112_ADS1115_I2C_ADDRESS_2 0x49 113 | 114 | #define RV112_ADS1115_GAIN ADS1115_GAIN_VREF_4_096 115 | //#define RV112_ADS1115_RATE ADS1115_RATE_475SPS 116 | #define RV112_ADS1115_RATE ADS1115_RATE_860SPS 117 | 118 | ads1115_t ads1115_nodes[MAX_NUM_ADS1115]; 119 | 120 | void init_zynpots() { 121 | reset_zyncoders(); 122 | reset_zynpots(); 123 | init_rv112s(); 124 | init_ads1115(&(ads1115_nodes[0]), RV112_ADS1115_I2C_ADDRESS_1, RV112_ADS1115_GAIN, RV112_ADS1115_RATE); 125 | init_ads1115(&(ads1115_nodes[1]), RV112_ADS1115_I2C_ADDRESS_2, RV112_ADS1115_GAIN, RV112_ADS1115_RATE); 126 | 127 | #if Z2_VERSION>2 128 | fprintf(stderr, "ZynCore: Setting-up Zynpots => 3 x RV112, 1 x PEC11 ...\n"); 129 | setup_rv112(0, &(ads1115_nodes[0]), 0); 130 | setup_rv112(1, &(ads1115_nodes[0]), 0); 131 | setup_rv112(2, &(ads1115_nodes[1]), 0); 132 | init_poll_rv112(); 133 | setup_zyncoder(0, MCP23017_2_BASE_PIN + 14, MCP23017_2_BASE_PIN + 15); 134 | setup_zynpot(0, ZYNPOT_RV112, 0); 135 | setup_zynpot(1, ZYNPOT_RV112, 1); 136 | setup_zynpot(2, ZYNPOT_RV112, 2); 137 | setup_zynpot(3, ZYNPOT_ZYNCODER, 0); 138 | #else 139 | fprintf(stderr, "ZynCore: Setting-up Zynpots => 4 x RV112...\n"); 140 | setup_rv112(0, &(ads1115_nodes[0]), 0); 141 | setup_rv112(1, &(ads1115_nodes[0]), 0); 142 | setup_rv112(2, &(ads1115_nodes[1]), 0); 143 | setup_rv112(3, &(ads1115_nodes[1]), 1); 144 | init_poll_rv112(); 145 | int i; 146 | for (i = 0; i < 4; i++) { 147 | setup_zynpot(i, ZYNPOT_RV112, i); 148 | } 149 | #endif 150 | } 151 | 152 | void end_zynpots() { 153 | end_rv112s(); 154 | reset_zynpots(); 155 | } 156 | 157 | //----------------------------------------------------------------------------- 158 | // Zyncontrol Initialization 159 | //----------------------------------------------------------------------------- 160 | 161 | uint8_t set_hpvol(uint8_t vol) { return lm4811_set_volume(vol); } 162 | uint8_t get_hpvol() { return lm4811_get_volume(); } 163 | uint8_t get_hpvol_max() { return lm4811_get_volume_max(); } 164 | 165 | int init_zyncontrol() { 166 | gpiod_init_callbacks(); 167 | lm4811_init(); 168 | init_zynmcp23017s(); 169 | init_zynswitches(); 170 | init_zynpots(); 171 | gpiod_start_callbacks(); 172 | return 1; 173 | } 174 | 175 | int end_zyncontrol() { 176 | gpiod_stop_callbacks(); 177 | end_zynpots(); 178 | reset_zyncoders(); 179 | reset_zynswitches(); 180 | reset_zynmcp23017s(); 181 | lm4811_end(); 182 | return 1; 183 | } 184 | 185 | //----------------------------------------------------------------------------- 186 | -------------------------------------------------------------------------------- /zyncore.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zynthian Core Library Initialization 4 | * 5 | * Initialize core modules 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include "gpiod_callback.h" 27 | #include "zyncontrol.h" 28 | #include "zynmidirouter.h" 29 | #include "zynmaster.h" 30 | 31 | //----------------------------------------------------------------------------- 32 | 33 | int init_zyncore() { 34 | if (!init_zyncontrol()) return 1; 35 | if (!init_zynmidirouter()) return 2; 36 | if (!init_zynmaster_jack()) return 3; 37 | return 0; 38 | } 39 | 40 | int init_zyncore_minimal() { 41 | //gpiod_init_callbacks(); 42 | return 0; 43 | } 44 | 45 | int end_zyncore() { 46 | if (!end_zynmaster_jack()) return 0; 47 | if (!end_zynmidirouter()) return 0; 48 | if (!end_zyncontrol()) return 0; 49 | return 1; 50 | } 51 | 52 | //----------------------------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /zyncore.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | # ******************************************************************** 4 | # ZYNTHIAN PROJECT: Zynthian Core library python wrapper 5 | # 6 | # A Python wrapper for Zynthian Core library 7 | # 8 | # Copyright (C) 2015-2021 Fernando Moyano 9 | # 10 | # ******************************************************************** 11 | # 12 | # This program is free software; you can redistribute it and/or 13 | # modify it under the terms of the GNU General Public License as 14 | # published by the Free Software Foundation; either version 2 of 15 | # the License, or any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | # 22 | # For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | # 24 | # ******************************************************************** 25 | 26 | from ctypes import * 27 | from os.path import dirname, realpath 28 | #from numpy.ctypeslib import ndpointer 29 | 30 | # ------------------------------------------------------------------------------- 31 | # Zyncoder Library Wrapper 32 | # ------------------------------------------------------------------------------- 33 | 34 | global lib_zyncore 35 | lib_zyncore = None 36 | 37 | 38 | def lib_zyncore_init(): 39 | global lib_zyncore 40 | try: 41 | lib_zyncore = cdll.LoadLibrary(dirname(realpath(__file__))+"/build/libzyncore.so") 42 | result = lib_zyncore.init_zyncore() 43 | except Exception as e: 44 | lib_zyncore = None 45 | raise Exception(f"Can't init zyncore library: {e}") 46 | 47 | if result != 0: 48 | lib_zyncore = None 49 | if result == 1: 50 | raise Exception("Failed to initialise zyncontrol", 1) 51 | elif result == 2: 52 | raise Exception("Failed to initialise zynmidirouter", 2) 53 | elif result == 3: 54 | raise Exception("Failed to initialise zynmaster_jack", 3) 55 | 56 | # Setup return type for some functions 57 | #lib_zyncore.get_midi_filter_clone_cc.restype = ndpointer(dtype=c_ubyte, shape=(128,)) 58 | 59 | #raise Exception("Failed to initialise zyncontrol", 1) 60 | #raise Exception("Failed to initialise zynmidirouter", 2) 61 | return lib_zyncore 62 | 63 | 64 | def lib_zyncore_init_minimal(): 65 | global lib_zyncore 66 | try: 67 | lib_zyncore = cdll.LoadLibrary(dirname(realpath(__file__))+"/build/libzyncore.so") 68 | lib_zyncore.init_zyncore_minimal() 69 | 70 | except Exception as e: 71 | lib_zyncore = None 72 | print("Can't init minimal zyncore library: %s" % str(e)) 73 | 74 | return lib_zyncore 75 | 76 | 77 | def get_lib_zyncore(): 78 | return lib_zyncore 79 | 80 | # ------------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /zynmaster.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Master Output Library 4 | * 5 | * Jack client for managing Master Audio & MIDI output 6 | * 7 | * Copyright (C) 2015-2019 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef ZYNAPTIK_CONFIG 32 | #include "zynaptik.h" 33 | #endif 34 | 35 | //----------------------------------------------------------------------------- 36 | 37 | jack_client_t *zynmaster_jack_client; 38 | jack_port_t *zynmaster_jack_port_midi_in; 39 | 40 | int zynmaster_jack_process(jack_nframes_t nframes, void *arg); 41 | 42 | //----------------------------------------------------------------------------- 43 | 44 | int init_zynmaster_jack() { 45 | if ((zynmaster_jack_client=jack_client_open("ZynMaster", JackNullOption, 0, 0))==NULL) { 46 | fprintf(stderr, "ZynMaster: Error connecting with jack server.\n"); 47 | return 0; 48 | } 49 | 50 | zynmaster_jack_port_midi_in=jack_port_register(zynmaster_jack_client, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); 51 | if (zynmaster_jack_port_midi_in==NULL) { 52 | fprintf(stderr, "ZynMaster: Error creating jack midi input port.\n"); 53 | return 0; 54 | } 55 | 56 | //Init Jack Process 57 | jack_set_process_callback(zynmaster_jack_client, zynmaster_jack_process, 0); 58 | if (jack_activate(zynmaster_jack_client)) { 59 | fprintf(stderr, "ZynMaster: Error activating jack client.\n"); 60 | return 0; 61 | } 62 | 63 | return 1; 64 | } 65 | 66 | int end_zynmaster_jack() { 67 | if (jack_client_close(zynmaster_jack_client)) { 68 | fprintf(stderr, "ZynMaster: Error closing jack client.\n"); 69 | return 0; 70 | } 71 | return 1; 72 | } 73 | 74 | int zynmaster_jack_process(jack_nframes_t nframes, void *arg) { 75 | // Read jackd data buffer 76 | void *input_port_buffer = jack_port_get_buffer(zynmaster_jack_port_midi_in, nframes); 77 | if (input_port_buffer==NULL) { 78 | fprintf(stderr, "ZynMaster: Error getting jack input port buffer: %d frames\n", nframes); 79 | return -1; 80 | } 81 | 82 | // Process MIDI input messages => Convert to CV/Gate out 83 | int i=0; 84 | jack_midi_event_t ev; 85 | while (jack_midi_event_get(&ev, input_port_buffer, i++)==0) { 86 | //fprintf(stderr, "ZynMaster: Converting MIDI event to CV/Gate => 0x%x, 0x%x, 0x%x\n", ev.buffer[0], ev.buffer[1], ev.buffer[2]); 87 | #ifdef ZYNAPTIK_CONFIG 88 | zynaptik_midi_to_cvout(&ev); 89 | zynaptik_midi_to_gateout(&ev); 90 | #endif 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | //----------------------------------------------------------------------------- 97 | -------------------------------------------------------------------------------- /zynmaster.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Master Output Library 4 | * 5 | * Jack client for managing Master Audio & MIDI output 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | 28 | //----------------------------------------------------------------------------- 29 | // Jack MIDI Process 30 | //----------------------------------------------------------------------------- 31 | 32 | int init_zynmaster_jack(); 33 | int end_zynmaster_jack(); 34 | int zynmaster_jack_process(jack_nframes_t nframes, void *arg); 35 | 36 | //----------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /zynmcp23008.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library 4 | * 5 | * Library for interfacing MCP23008 using polling (only zynswitches!). 6 | * 7 | * Copyright (C) 2015-2022 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | //#define DEBUG 36 | 37 | #include "wiringPiI2C.h" 38 | #include "zynmcp23008.h" 39 | #include "zyncoder.h" 40 | 41 | //----------------------------------------------------------------------------- 42 | // Macros 43 | //----------------------------------------------------------------------------- 44 | 45 | #define bitRead(value, bit) (((value) >> (bit)) & 0x01) 46 | #define bitSet(value, bit) ((value) |= (1UL << (bit))) 47 | #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) 48 | #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) 49 | 50 | //----------------------------------------------------------------------------- 51 | // Global variables 52 | //----------------------------------------------------------------------------- 53 | 54 | extern zynswitch_t zynswitches[MAX_NUM_ZYNSWITCHES]; 55 | 56 | zynmcp23008_t zynmcp23008s[MAX_NUM_MCP23008]; 57 | int end_poll_zynswitches_flag = 0; 58 | 59 | //----------------------------------------------------------------------------- 60 | // MCP23008 functions 61 | //----------------------------------------------------------------------------- 62 | 63 | void reset_zynmcp23008s() { 64 | int i; 65 | for (i=0;i= MAX_NUM_MCP23008) { 73 | fprintf(stderr, "ZynCore->setup_zynmcp23008(%d, ...): Invalid index!\n", i); 74 | return 0; 75 | } 76 | 77 | // Setup IC using I2C bus 78 | int fd = wiringPiI2CSetup(i2c_address); 79 | if (fd < 0) { 80 | fprintf(stderr, "ZynCore->setup_zynmcp23008(%d, ...): Can't open I2C device at %d!\n", i, i2c_address); 81 | return 0; 82 | } 83 | // Initialize IC 84 | wiringPiI2CWriteReg8(fd, MCP23x08_IOCON, IOCON_INIT); 85 | uint8_t olat = wiringPiI2CReadReg8 (fd, MCP23x08_OLAT); 86 | 87 | // setup all the pins as inputs and disable pullups 88 | wiringPiI2CWriteReg8(fd, MCP23x08_IODIR, 0xff); 89 | 90 | // enable pullups on the unused pins (high two bits) 91 | wiringPiI2CWriteReg8(fd, MCP23x08_GPPU, 0xff); 92 | 93 | // disable polarity inversion 94 | //wiringPiI2CWriteReg8(fd, MCP23x08_IPOL, 0x0); 95 | 96 | // disable the comparison to DEFVAL register 97 | //wiringPiI2CWriteReg8(fd, MCP23x08_INTCON, 0x0); 98 | 99 | // Setup data struct 100 | zynmcp23008s[i].fd = fd; 101 | zynmcp23008s[i].base_pin = base_pin; 102 | zynmcp23008s[i].i2c_address = i2c_address; 103 | zynmcp23008s[i].output_state = olat; 104 | wiringPiI2CReadReg8(fd, MCP23x08_GPIO); 105 | zynmcp23008s[i].enabled = 1; 106 | 107 | //fprintf(stderr, "ZynCore->setup_zynmcp23008(%d, ...): I2C %x, base-pin %d\n", i, i2c_address, base_pin); 108 | 109 | return 1; 110 | } 111 | 112 | int8_t zynmcp23008_get_last_index() { 113 | uint8_t i; 114 | uint8_t li = -1; 115 | for (i=0; i= zynmcp23008s[i].base_pin && pin < (zynmcp23008s[i].base_pin+8)) return i; 126 | } 127 | } 128 | return -1; 129 | } 130 | 131 | /* 132 | * zynmcp23008_set_pin_mode: 133 | * i : chip index 134 | * pin : pin number 135 | * mode : INPUT=1, OUTPUT=0 136 | */ 137 | void zynmcp23008_set_pin_mode (uint8_t i, uint16_t pin, uint8_t mode) { 138 | uint8_t mask, data ; 139 | data = wiringPiI2CReadReg8 (zynmcp23008s[i].fd, MCP23x08_IODIR) ; 140 | mask = 1 << ((pin - zynmcp23008s[i].base_pin) & 0x7) ; 141 | if (mode == PIN_MODE_OUTPUT) data &= (~mask) ; 142 | else data |= mask ; 143 | wiringPiI2CWriteReg8 (zynmcp23008s[i].fd, MCP23x08_IODIR, data) ; 144 | } 145 | 146 | /* 147 | * zynmcp23008_set_pull_up_down: 148 | * i : chip index 149 | * pin : pin number 150 | * mode : UP=1, DOWN=0 151 | */ 152 | void zynmcp23008_set_pull_up_down (uint8_t i, uint16_t pin, uint8_t mode) { 153 | uint8_t mask, data ; 154 | //int8_t i = pin2index_zynmcp23008(pin); 155 | data = wiringPiI2CReadReg8 (zynmcp23008s[i].fd, MCP23x08_GPPU) ; 156 | mask = 1 << ((pin - zynmcp23008s[i].base_pin) & 0x7) ; 157 | if (mode == PIN_PUD_DOWN) data &= (~mask) ; 158 | else data |= mask ; 159 | wiringPiI2CWriteReg8 (zynmcp23008s[i].fd, MCP23x08_GPPU, data) ; 160 | } 161 | 162 | /* 163 | * zynmcp23008_write_pin: 164 | * i : chip index 165 | * pin : pin number 166 | * val : value to write (1/0) 167 | */ 168 | void zynmcp23008_write_pin (uint8_t i, uint16_t pin, uint8_t val) { 169 | uint8_t mask, data ; 170 | data = zynmcp23008s[i].output_state ; 171 | mask = 1 << ((pin - zynmcp23008s[i].base_pin) & 0x7) ; 172 | if (val == 0) data &= (~mask) ; 173 | else data |= mask ; 174 | wiringPiI2CWriteReg8 (zynmcp23008s[i].fd, MCP23x08_GPIO, data) ; 175 | zynmcp23008s[i].output_state = data; 176 | } 177 | 178 | /* 179 | * zynmcp23008_read_pin: 180 | * i : chip index 181 | * pin : pin number 182 | * Returns read value (1/0) 183 | */ 184 | uint8_t zynmcp23008_read_pin (uint8_t i, uint16_t pin) { 185 | uint8_t mask, data ; 186 | mask = 1 << ((pin - zynmcp23008s[i].base_pin) & 0x7) ; 187 | data = wiringPiI2CReadReg8 (zynmcp23008s[i].fd, MCP23x08_GPIO) ; 188 | if ((data & mask) == 0) return 0 ; 189 | else return 1 ; 190 | } 191 | 192 | /* 193 | * zynmcp23008_read_pins: 194 | * i : chip index 195 | * Returns read values (as bits) 196 | */ 197 | uint8_t zynmcp23008_read_pins(uint8_t i) { 198 | return wiringPiI2CReadReg8(zynmcp23008s[i].fd, MCP23x08_GPIO); 199 | } 200 | 201 | //----------------------------------------------------------------------------- 202 | // MCP23008 Polling (only switches) 203 | //----------------------------------------------------------------------------- 204 | 205 | // Update polled (non-ISR) switches (expanded GPIO with MCP23008 without connecting INT line 206 | // This is only used by legacy V1's 2in1 module!! 207 | void update_polled_zynswitches(int8_t i) { 208 | struct timespec ts; 209 | unsigned long int tsus; 210 | clock_gettime(CLOCK_MONOTONIC, &ts); 211 | tsus=ts.tv_sec*1000000 + ts.tv_nsec/1000; 212 | 213 | uint8_t rdata = wiringPiI2CReadReg8(zynmcp23008s[i].fd, MCP23x08_GPIO); 214 | //fprintf(stderr, "POLLING MCP23008 (%d) => 0x%x\n", i, rdata); 215 | 216 | int j; 217 | uint8_t bit; 218 | uint8_t status; 219 | for (j=0; j=100) are polled! 222 | if (!zsw->enabled || zsw->pin<100) continue; 223 | bit = zsw->pin - zynmcp23008s[i].base_pin; 224 | if (bit<8) { 225 | status=bitRead(rdata, bit); 226 | } else { 227 | fprintf(stderr, "ZynCoder->update_polled_zynswitches(%d): Wrong pin number %d in zynswitch %d!\n", i, zsw->pin, j); 228 | status = 0; 229 | } 230 | //fprintf(stderr, "POLLING SWITCH %d on pin %d => %d\n", j, zsw->pin, status); 231 | if (status == zsw->status) continue; 232 | zsw->status=status; 233 | send_zynswitch_midi(zsw); 234 | //fprintf(stderr, "POLLING SWITCH %d => STATUS=%d (%lu)\n", i, zsw->status, tsus); 235 | if (zsw->status==1) { 236 | if (zsw->tsus>0) { 237 | unsigned int dtus=tsus-zsw->tsus; 238 | zsw->tsus=0; 239 | //Ignore spurious ticks 240 | if (dtus<1000) return; 241 | //fprintf(stderr, "Debounced Switch %d\n",i); 242 | zsw->dtus=dtus; 243 | } 244 | } else zsw->tsus=tsus; 245 | } 246 | } 247 | 248 | void * poll_zynswitches(void *arg) { 249 | while (!end_poll_zynswitches_flag) { 250 | // Note this only polls first MCP23008 chip!!! 251 | update_polled_zynswitches(0); 252 | usleep(POLL_ZYNSWITCHES_US); 253 | } 254 | fprintf(stderr, "ZynCore->poll_zynswitches(): Zynswitches poll thread ended flawlessly\n"); 255 | return NULL; 256 | } 257 | 258 | pthread_t init_poll_zynswitches() { 259 | pthread_t tid; 260 | end_poll_zynswitches_flag = 0; 261 | int err=pthread_create(&tid, NULL, &poll_zynswitches, NULL); 262 | if (err != 0) { 263 | fprintf(stderr, "ZynCore->init_poll_zynswitches(): Can't create zynswitches poll thread :[%s]", strerror(err)); 264 | return 0; 265 | } else { 266 | fprintf(stderr, "ZynCore->init_poll_zynswitches(): Zynswitches poll thread created successfully\n"); 267 | return tid; 268 | } 269 | } 270 | 271 | void end_poll_zynswitches() { 272 | end_poll_zynswitches_flag = 1; 273 | } 274 | 275 | //----------------------------------------------------------------------------- 276 | -------------------------------------------------------------------------------- /zynmcp23008.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library 4 | * 5 | * Library for interfacing MCP23008 using polling (only zynswitches!). 6 | * 7 | * Copyright (C) 2015-2022 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | // WARNING! It's not possible to use this with zynmcp23017!! 27 | 28 | #ifndef ZYNMCP23008_H 29 | #define ZYNMCP23008_H 30 | 31 | #include 32 | #include 33 | 34 | //----------------------------------------------------------------------------- 35 | // IC registers 36 | //----------------------------------------------------------------------------- 37 | 38 | // MCP23x08 Registers 39 | #define MCP23x08_IODIR 0x00 40 | #define MCP23x08_IPOL 0x01 41 | #define MCP23x08_GPINTEN 0x02 42 | #define MCP23x08_DEFVAL 0x03 43 | #define MCP23x08_INTCON 0x04 44 | #define MCP23x08_IOCON 0x05 45 | #define MCP23x08_GPPU 0x06 46 | #define MCP23x08_INTF 0x07 47 | #define MCP23x08_INTCAP 0x08 48 | #define MCP23x08_GPIO 0x09 49 | #define MCP23x08_OLAT 0x0A 50 | 51 | // Bits in the IOCON register 52 | #define IOCON_UNUSED 0x01 53 | #define IOCON_INTPOL 0x02 54 | #define IOCON_ODR 0x04 55 | #define IOCON_HAEN 0x08 56 | #define IOCON_DISSLW 0x10 57 | #define IOCON_SEQOP 0x20 58 | #define IOCON_MIRROR 0x40 59 | #define IOCON_BANK_MODE 0x80 60 | 61 | // Default initialisation mode 62 | #define IOCON_INIT (IOCON_SEQOP) 63 | 64 | // SPI Command codes 65 | #define CMD_WRITE 0x40 66 | #define CMD_READ 0x41 67 | 68 | // Pin modes 69 | #define PIN_MODE_OUTPUT 0x0 70 | #define PIN_MODE_INPUT 0x1 71 | #define PIN_PUD_DOWN 0x0 72 | #define PIN_PUD_UP 0x1 73 | 74 | //----------------------------------------------------------------------------- 75 | // MCP23008 stuff 76 | //----------------------------------------------------------------------------- 77 | 78 | #define MAX_NUM_MCP23008 4 79 | //Switch Polling interval 80 | #define POLL_ZYNSWITCHES_US 10000 81 | 82 | 83 | typedef struct zynmcp23008_st { 84 | uint8_t enabled; 85 | int fd; 86 | uint16_t base_pin; 87 | uint8_t i2c_address; 88 | uint8_t output_state; 89 | } zynmcp23008_t; 90 | 91 | void reset_zynmcp23008s(); 92 | int setup_zynmcp23008(uint8_t i, uint16_t base_pin, uint8_t i2c_address); 93 | 94 | int8_t zynmcp23008_get_last_index(); 95 | int8_t zynmcp23008_pin2index(uint16_t pin); 96 | 97 | void zynmcp23008_set_pin_mode (uint8_t i, uint16_t pin, uint8_t mode); 98 | void zynmcp23008_set_pull_up_down (uint8_t i, uint16_t pin, uint8_t mode); 99 | void zynmcp23008_write_pin (uint8_t i, uint16_t pin, uint8_t val); 100 | uint8_t zynmcp23008_read_pin (uint8_t i, uint16_t pin); 101 | uint8_t zynmcp23008_read_pins_(uint8_t i); 102 | 103 | //Switches Polling Thread (should be avoided!) 104 | pthread_t init_poll_zynswitches(); 105 | void end_poll_zynswitches(); 106 | 107 | //----------------------------------------------------------------------------- 108 | 109 | #endif -------------------------------------------------------------------------------- /zynmcp23017.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library 4 | * 5 | * Library for interfacing MCP23017 using IRQs. 6 | * 7 | * Copyright (C) 2015-2022 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | //#define DEBUG 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "gpiod_callback.h" 39 | #include "wiringPiI2C.h" 40 | #include "zynmcp23017.h" 41 | #include "zyncoder.h" 42 | 43 | //----------------------------------------------------------------------------- 44 | // Macros 45 | //----------------------------------------------------------------------------- 46 | 47 | #define bitRead(value, bit) (((value) >> (bit)) & 0x01) 48 | #define bitSet(value, bit) ((value) |= (1UL << (bit))) 49 | #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) 50 | #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) 51 | 52 | //----------------------------------------------------------------------------- 53 | // Global variables 54 | //----------------------------------------------------------------------------- 55 | 56 | extern zynswitch_t zynswitches[MAX_NUM_ZYNSWITCHES]; 57 | extern zyncoder_t zyncoders[MAX_NUM_ZYNCODERS]; 58 | 59 | zynmcp23017_t zynmcp23017s[MAX_NUM_MCP23017]; 60 | 61 | //----------------------------------------------------------------------------- 62 | // MCP23017 functions 63 | //----------------------------------------------------------------------------- 64 | 65 | void reset_zynmcp23017s() { 66 | int i; 67 | for (i=0;i= MAX_NUM_MCP23017) { 75 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Invalid index!\n", i); 76 | return 0; 77 | } 78 | 79 | // Setup IC using I2C bus 80 | int fd = wiringPiI2CSetup(i2c_address); 81 | if (fd < 0) { 82 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't open I2C device at %d!\n", i, i2c_address); 83 | return 0; 84 | } 85 | // Initialize IC 86 | wiringPiI2CWriteReg8(fd, MCP23x17_IOCON, IOCON_INIT); 87 | uint8_t olata = wiringPiI2CReadReg8 (fd, MCP23x17_OLATA); 88 | uint8_t olatb = wiringPiI2CReadReg8 (fd, MCP23x17_OLATB); 89 | 90 | // setup all the pins on the banks as inputs and disable pullups on 91 | // the zyncoder input 92 | uint8_t reg = 0xff; 93 | wiringPiI2CWriteReg8(fd, MCP23x17_IODIRA, reg); 94 | wiringPiI2CWriteReg8(fd, MCP23x17_IODIRB, reg); 95 | 96 | // enable pullups on the unused pins (high two bits on each bank) 97 | reg = 0xff; 98 | //reg = 0xc0; 99 | //reg = 0x60; 100 | wiringPiI2CWriteReg8(fd, MCP23x17_GPPUA, reg); 101 | wiringPiI2CWriteReg8(fd, MCP23x17_GPPUB, reg); 102 | 103 | // disable polarity inversion 104 | reg = 0; 105 | wiringPiI2CWriteReg8(fd, MCP23x17_IPOLA, reg); 106 | wiringPiI2CWriteReg8(fd, MCP23x17_IPOLB, reg); 107 | 108 | // disable the comparison to DEFVAL register 109 | reg = 0; 110 | wiringPiI2CWriteReg8(fd, MCP23x17_INTCONA, reg); 111 | wiringPiI2CWriteReg8(fd, MCP23x17_INTCONB, reg); 112 | 113 | // configure the interrupt behavior for bank A 114 | uint8_t ioconf_value = wiringPiI2CReadReg8(fd, MCP23x17_IOCON); 115 | bitWrite(ioconf_value, 6, 0); // banks are not mirrored 116 | bitWrite(ioconf_value, 2, 0); // interrupt pin is not floating 117 | bitWrite(ioconf_value, 1, 1); // interrupt is signaled by high 118 | wiringPiI2CWriteReg8(fd, MCP23x17_IOCON, ioconf_value); 119 | 120 | // configure the interrupt behavior for bank B 121 | ioconf_value = wiringPiI2CReadReg8(fd, MCP23x17_IOCONB); 122 | bitWrite(ioconf_value, 6, 0); // banks are not mirrored 123 | bitWrite(ioconf_value, 2, 0); // interrupt pin is not floating 124 | bitWrite(ioconf_value, 1, 1); // interrupt is signaled by high 125 | wiringPiI2CWriteReg8(fd, MCP23x17_IOCONB, ioconf_value); 126 | 127 | // finally, enable the interrupt pins for banks a and b 128 | // enable interrupts on all pins 129 | reg = 0xff; 130 | wiringPiI2CWriteReg8(fd, MCP23x17_GPINTENA, reg); 131 | wiringPiI2CWriteReg8(fd, MCP23x17_GPINTENB, reg); 132 | 133 | // Setup data struct 134 | zynmcp23017s[i].fd = fd; 135 | zynmcp23017s[i].base_pin = base_pin; 136 | zynmcp23017s[i].i2c_address = i2c_address; 137 | zynmcp23017s[i].output_state_A = olata; 138 | zynmcp23017s[i].output_state_B = olatb; 139 | zynmcp23017s[i].intA_pin = intA_pin; 140 | zynmcp23017s[i].intB_pin = intB_pin; 141 | zynmcp23017s[i].last_state_A = wiringPiI2CReadReg8(fd, MCP23x17_GPIOA); 142 | zynmcp23017s[i].last_state_B = wiringPiI2CReadReg8(fd, MCP23x17_GPIOB); 143 | int j; 144 | for (j=0; j<16; j++) { 145 | zynmcp23017s[i].pin_action[j] = NONE_PIN_ACTION; 146 | zynmcp23017s[i].pin_action_num[j] = 0; 147 | } 148 | zynmcp23017s[i].enabled = 1; 149 | 150 | // Setup callbacks for the interrupt pins 151 | struct gpiod_line *line_a = gpiod_chip_get_line(gpio_chip, intA_pin); 152 | if (line_a) { 153 | if (gpiod_line_request_rising_edge_events_flags(line_a, ZYNCORE_CONSUMER, 0) < 0) { 154 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't request line for INTA pin %d\n", i, intA_pin); 155 | return 0; 156 | } 157 | if (gpiod_line_register_callback(line_a, isrs[0]) < 0) { 158 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't register callback for INTA pin %d\n", i, intA_pin); 159 | return 0; 160 | } 161 | } else { 162 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't get line for INTA pin %d\n", i, intA_pin); 163 | return 0; 164 | } 165 | struct gpiod_line *line_b = gpiod_chip_get_line(gpio_chip, intB_pin); 166 | if (line_b) { 167 | if (gpiod_line_request_rising_edge_events_flags(line_b, ZYNCORE_CONSUMER, 0) < 0) { 168 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't request line for INTB pin %d\n", i, intB_pin); 169 | return 0; 170 | } 171 | if (gpiod_line_register_callback(line_b, isrs[1]) < 0) { 172 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't register callback for INTB pin %d\n", i, intB_pin); 173 | return 0; 174 | } 175 | } else { 176 | fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): Can't get line for INTB pin %d\n", i, intB_pin); 177 | return 0; 178 | } 179 | //fprintf(stderr, "ZynCore->setup_zynmcp23017(%d, ...): I2C %x, base-pin %d, INTA %d, INTB %d\n", i, i2c_address, base_pin, intA_pin, intB_pin); 180 | 181 | return 1; 182 | } 183 | 184 | int get_last_zynmcp23017_index() { 185 | int i; 186 | int li = 0; 187 | for (i=0;i= zynmcp23017s[i].base_pin && pin < (zynmcp23017s[i].base_pin+16)) return i; 198 | } 199 | } 200 | return -1; 201 | } 202 | 203 | int setup_pin_action_zynmcp23017(uint16_t pin, zynmcp23017_pin_action_t action, uint16_t num) { 204 | int i = pin2index_zynmcp23017(pin); 205 | if (i < 0) { 206 | fprintf(stderr, "ZynCore->setup_pin_action_zynmcp23017(%d, ...): Not a MCP23017 pin!\n", pin); 207 | return 0; 208 | } 209 | int j = pin - zynmcp23017s[i].base_pin; 210 | if (j>=0 && j<16) { 211 | zynmcp23017s[i].pin_action[j] = action; 212 | zynmcp23017s[i].pin_action_num[j] = num; 213 | //fprintf(stderr, "ZynCore->setup_pin_action_zynmcp23017(%d, %d, %d)\n", pin, action, num); 214 | } 215 | else { 216 | fprintf(stderr, "ZynCore->setup_pin_action_zynmcp23017(%d, ...): Pin out of range!\n", pin); 217 | return 0; 218 | } 219 | return 1; 220 | } 221 | 222 | int reset_pin_action_zynmcp23017(uint16_t pin) { 223 | int i = pin2index_zynmcp23017(pin); 224 | if (i < 0) { 225 | fprintf(stderr, "ZynCore->reset_pin_action_zynmcp23017(%d): Not a MCP23017 pin!\n", pin); 226 | return 0; 227 | } 228 | int j = pin - zynmcp23017s[i].base_pin; 229 | if (j>=0 && j<16) { 230 | zynmcp23017s[i].pin_action[j] = NONE_PIN_ACTION; 231 | zynmcp23017s[i].pin_action_num[j] = 0; 232 | } 233 | else { 234 | fprintf(stderr, "ZynCore->reset_pin_action_zynmcp23017(%d, ...): Pin out of range!\n", pin); 235 | return 0; 236 | } 237 | return 1; 238 | } 239 | 240 | /* 241 | * set_pin_mode_zynmcp23017: 242 | * pin : pin number 243 | * mode : PIN_MODE_INPUT=1, PIN_MODE_OUTPUT=0 244 | */ 245 | int set_pin_mode_zynmcp23017(uint16_t pin, uint8_t mode) { 246 | int i = pin2index_zynmcp23017(pin); 247 | if (i >= 0) { 248 | int mask, old, reg; 249 | pin -= zynmcp23017s[i].base_pin; // Pin now 0-15 250 | // Bank A 251 | if (pin < 8) reg = MCP23x17_IODIRA; 252 | // Bank B 253 | else { 254 | reg = MCP23x17_IODIRB; 255 | pin &= 0x07; 256 | } 257 | mask = 1 << pin ; 258 | old = wiringPiI2CReadReg8(zynmcp23017s[i].fd, reg); 259 | if (mode == PIN_MODE_OUTPUT) old &= (~mask); 260 | else old |= mask; 261 | wiringPiI2CWriteReg8 (zynmcp23017s[i].fd, reg, old) ; 262 | return 0; 263 | } 264 | fprintf(stderr, "ZynCore: set_pin_mode_zynmcp23017(%d) => invalid pin!\n", pin); 265 | return -1; 266 | } 267 | 268 | /* 269 | * set_pull_up_down_zynmcp23017: 270 | * pin : pin number 271 | * mode : PIN_PUD_UP=1, PIN_PUD_DOWN=0 272 | */ 273 | int set_pull_up_down_zynmcp23017(uint16_t pin, uint8_t mode) { 274 | int i = pin2index_zynmcp23017(pin); 275 | if (i >= 0) { 276 | int mask, old, reg ; 277 | pin -= zynmcp23017s[i].base_pin; // Pin now 0-15 278 | // Bank A 279 | if (pin < 8) reg = MCP23x17_GPPUA; 280 | // Bank B 281 | else { 282 | reg = MCP23x17_GPPUA; 283 | pin &= 0x07; 284 | } 285 | mask = 1 << pin; 286 | old = wiringPiI2CReadReg8(zynmcp23017s[i].fd, reg); 287 | if (mode == PIN_PUD_DOWN) old &= (~mask); 288 | else old |= mask; 289 | wiringPiI2CWriteReg8 (zynmcp23017s[i].fd, reg, old); 290 | return 0; 291 | } 292 | fprintf(stderr, "ZynCore: set_pull_up_down_zynmcp23017(%d) => invalid pin!\n", pin); 293 | return -1; 294 | } 295 | 296 | /* 297 | * write_pin_zynmcp23017: 298 | * pin : pin number 299 | * val : value to write (1/0) 300 | */ 301 | int write_pin_zynmcp23017(uint16_t pin, uint8_t val) { 302 | int i = pin2index_zynmcp23017(pin); 303 | if (i>=0) { 304 | int bit, old ; 305 | pin -= zynmcp23017s[i].base_pin ; // Pin now 0-15 306 | bit = 1 << (pin & 7) ; 307 | // Bank A 308 | if (pin < 8) { 309 | old = zynmcp23017s[i].output_state_A; 310 | if (val == 0) old &= (~bit); 311 | else old |= bit; 312 | wiringPiI2CWriteReg8(zynmcp23017s[i].fd, MCP23x17_GPIOA, old); 313 | zynmcp23017s[i].output_state_A = old; 314 | } 315 | // Bank B 316 | else { 317 | old = zynmcp23017s[i].output_state_B; 318 | if (val == 0) old &= (~bit); 319 | else old |= bit; 320 | wiringPiI2CWriteReg8(zynmcp23017s[i].fd, MCP23x17_GPIOB, old); 321 | zynmcp23017s[i].output_state_B = old; 322 | } 323 | return 0; 324 | } 325 | fprintf(stderr, "ZynCore: write_pin_zynmcp23017(%d) => invalid pin!\n", pin); 326 | return -1; 327 | } 328 | 329 | int read_pin_zynmcp23017(uint16_t pin) { 330 | int i = pin2index_zynmcp23017(pin); 331 | if (i>=0) { 332 | uint8_t bit = pin - zynmcp23017s[i].base_pin; 333 | uint16_t reg; 334 | // Bank A 335 | if (bit<8) { 336 | reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_GPIOA); 337 | zynmcp23017s[i].last_state_A = reg; 338 | return bitRead(reg, bit); 339 | // Bank B 340 | } else if (bit<16) { 341 | reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_GPIOB); 342 | zynmcp23017s[i].last_state_B = reg; 343 | return bitRead(reg, (bit - 8)); 344 | } else { 345 | fprintf(stderr, "ZynCore: read_pin_zynmcp23017(%d) => pin %d out of range!\n", pin, pin); 346 | return -1; 347 | } 348 | } 349 | fprintf(stderr, "ZynCore: read_pin_zynmcp23017(%d) => invalid pin!\n", pin); 350 | return -1; 351 | } 352 | 353 | void zynswitch_update_zynmcp23017(uint8_t i) { 354 | if (i>=MAX_NUM_ZYNSWITCHES) return; 355 | zynswitch_t *zsw = zynswitches + i; 356 | if (zsw->enabled==0) return; 357 | 358 | int res = read_pin_zynmcp23017(zsw->pin); 359 | if (res>=0) update_zynswitch(i, (uint8_t)res); 360 | } 361 | 362 | void zyncoder_update_zynmcp23017(uint8_t i) { 363 | if (i>=MAX_NUM_ZYNCODERS) return; 364 | zyncoder_t *zcdr = zyncoders + i; 365 | if (zcdr->enabled==0) return; 366 | 367 | int res_a = read_pin_zynmcp23017(zcdr->pin_a); 368 | int res_b = read_pin_zynmcp23017(zcdr->pin_b); 369 | if (res_a >=0 && res_b >=0) update_zyncoder(i, (uint8_t)res_a, (uint8_t)res_b); 370 | } 371 | 372 | 373 | // ISR for handling the mcp23017 interrupts 374 | void zynmcp23017_ISR(uint8_t i, uint8_t bank) { 375 | if (i >= MAX_NUM_MCP23017) { 376 | fprintf(stderr, "ZynCore->zynmcp23017_ISR(%d, %d): Invalid index!\n", i, bank); 377 | return; 378 | } 379 | if (!zynmcp23017s[i].enabled) return; 380 | 381 | //fprintf(stderr, "zyncoder_mcp23017_ISR(%d, %d)\n", i, bank); 382 | 383 | uint16_t pin_offset; 384 | uint16_t reg; 385 | uint8_t rdiff; 386 | 387 | if (bank == 0) { 388 | pin_offset = 0; 389 | reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_GPIOA); 390 | //reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_INTCAPA); 391 | rdiff = reg ^ zynmcp23017s[i].last_state_A; 392 | zynmcp23017s[i].last_state_A = reg; 393 | } else if (bank == 1) { 394 | pin_offset = 8; 395 | reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_GPIOB); 396 | //reg = wiringPiI2CReadReg8(zynmcp23017s[i].fd, MCP23x17_INTCAPB); 397 | rdiff = reg ^ zynmcp23017s[i].last_state_B; 398 | zynmcp23017s[i].last_state_B = reg; 399 | } else { 400 | fprintf(stderr, "ZynCore->zynmcp23017_ISR(%d, %d): Invalid bank!\n", i, bank); 401 | return; 402 | } 403 | 404 | uint8_t j = 0; 405 | uint8_t k, bit_a, bit_b; 406 | while (rdiff != 0) { 407 | if (rdiff & 0x01) { 408 | //fprintf(stderr, "zyncoder_mcp23017_ISR(%d, %d) => pin %d changed, action %d\n", i, bank, j, zynmcp23017s[i].pin_action[j]); 409 | switch(zynmcp23017s[i].pin_action[j + pin_offset]) { 410 | case ZYNSWITCH_PIN_ACTION: 411 | k = zynmcp23017s[i].pin_action_num[j + pin_offset]; 412 | zynswitch_t *zsw = zynswitches + k; 413 | bit_a = zsw->pin - (zynmcp23017s[i].base_pin + pin_offset); 414 | update_zynswitch(k, bitRead(reg, bit_a)); 415 | break; 416 | case ZYNCODER_PIN_ACTION: 417 | k = zynmcp23017s[i].pin_action_num[j + pin_offset]; 418 | zyncoder_t *zcdr = zyncoders + k; 419 | bit_a = zcdr->pin_a - (zynmcp23017s[i].base_pin + pin_offset); 420 | bit_b = zcdr->pin_b - (zynmcp23017s[i].base_pin + pin_offset); 421 | update_zyncoder(k, bitRead(reg, bit_a), bitRead(reg, bit_b)); 422 | // Avoid processing same zyncoder twice 423 | rdiff &= ~ ((0x1 << bit_a) | (0x1 << bit_b)) >> j; 424 | break; 425 | } 426 | } 427 | rdiff >>= 1; 428 | j++; 429 | } 430 | } 431 | 432 | //----------------------------------------------------------------------------- 433 | -------------------------------------------------------------------------------- /zynmcp23017.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zyncoder Library 4 | * 5 | * Library for interfacing MCP23017 using IRQs. 6 | * 7 | * Copyright (C) 2015-2022 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #ifndef ZYNMCP23017_H 27 | #define ZYNMCP23017_H 28 | 29 | #include 30 | 31 | //----------------------------------------------------------------------------- 32 | // IC registers 33 | //----------------------------------------------------------------------------- 34 | 35 | // MCP23x17 Registers 36 | #define MCP23x17_IODIRA 0x00 37 | #define MCP23x17_IPOLA 0x02 38 | #define MCP23x17_GPINTENA 0x04 39 | #define MCP23x17_DEFVALA 0x06 40 | #define MCP23x17_INTCONA 0x08 41 | #define MCP23x17_IOCON 0x0A 42 | #define MCP23x17_GPPUA 0x0C 43 | #define MCP23x17_INTFA 0x0E 44 | #define MCP23x17_INTCAPA 0x10 45 | #define MCP23x17_GPIOA 0x12 46 | #define MCP23x17_OLATA 0x14 47 | 48 | #define MCP23x17_IODIRB 0x01 49 | #define MCP23x17_IPOLB 0x03 50 | #define MCP23x17_GPINTENB 0x05 51 | #define MCP23x17_DEFVALB 0x07 52 | #define MCP23x17_INTCONB 0x09 53 | #define MCP23x17_IOCONB 0x0B 54 | #define MCP23x17_GPPUB 0x0D 55 | #define MCP23x17_INTFB 0x0F 56 | #define MCP23x17_INTCAPB 0x11 57 | #define MCP23x17_GPIOB 0x13 58 | #define MCP23x17_OLATB 0x15 59 | 60 | // Bits in the IOCON register 61 | #define IOCON_UNUSED 0x01 62 | #define IOCON_INTPOL 0x02 63 | #define IOCON_ODR 0x04 64 | #define IOCON_HAEN 0x08 65 | #define IOCON_DISSLW 0x10 66 | #define IOCON_SEQOP 0x20 67 | #define IOCON_MIRROR 0x40 68 | #define IOCON_BANK_MODE 0x80 69 | 70 | // Default initialisation mode 71 | #define IOCON_INIT (IOCON_SEQOP) 72 | 73 | // SPI Command codes 74 | #define CMD_WRITE 0x40 75 | #define CMD_READ 0x41 76 | 77 | // Pin modes 78 | #define PIN_MODE_OUTPUT 0x0 79 | #define PIN_MODE_INPUT 0x1 80 | #define PIN_PUD_DOWN 0x0 81 | #define PIN_PUD_UP 0x1 82 | 83 | //----------------------------------------------------------------------------- 84 | // MCP23017 stuff 85 | //----------------------------------------------------------------------------- 86 | 87 | #define MAX_NUM_MCP23017 4 88 | 89 | typedef enum zynmcp23017_pin_action_enum { 90 | NONE_PIN_ACTION = 0, 91 | ZYNSWITCH_PIN_ACTION = 1, 92 | ZYNCODER_PIN_ACTION = 2 93 | } zynmcp23017_pin_action_t; 94 | 95 | 96 | typedef struct zynmcp23017_st { 97 | uint8_t enabled; 98 | 99 | int fd; 100 | uint16_t base_pin; 101 | uint8_t i2c_address; 102 | uint8_t intA_pin; 103 | uint8_t intB_pin; 104 | 105 | uint8_t last_state_A; 106 | uint8_t last_state_B; 107 | uint8_t output_state_A; 108 | uint8_t output_state_B; 109 | 110 | zynmcp23017_pin_action_t pin_action[16]; 111 | uint16_t pin_action_num[16]; 112 | 113 | } zynmcp23017_t; 114 | 115 | 116 | void reset_zynmcp23017s(); 117 | int setup_zynmcp23017(uint8_t i, uint16_t base_pin, uint8_t i2c_address, uint8_t intA_pin, uint8_t intB_pin, void (*isrs[2])); 118 | 119 | int get_last_zynmcp23017_index(); 120 | int pin2index_zynmcp23017(uint16_t pin); 121 | 122 | int setup_pin_action_zynmcp23017(uint16_t pin, zynmcp23017_pin_action_t action, uint16_t num); 123 | int reset_pin_action_zynmcp23017(uint16_t pin); 124 | 125 | int set_pin_mode_zynmcp23017(uint16_t pin, uint8_t mode); 126 | int set_pull_up_down_zynmcp23017(uint16_t pin, uint8_t mode); 127 | int write_pin_zynmcp23017(uint16_t pin, uint8_t val); 128 | int read_pin_zynmcp23017(uint16_t pin); 129 | 130 | void zynswitch_update_zynmcp23017(uint8_t i); 131 | void zyncoder_update_zynmcp23017(uint8_t i); 132 | 133 | // ISR callback function 134 | void zynmcp23017_ISR(uint8_t i, uint8_t bank); 135 | 136 | //----------------------------------------------------------------------------- 137 | 138 | #endif -------------------------------------------------------------------------------- /zynmidirouter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: ZynMidiRouter Library 4 | * 5 | * MIDI router library: Implements the MIDI router & filter 6 | * 7 | * Copyright (C) 2015-2025 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | //----------------------------------------------------------------------------- 31 | // Library Initialization 32 | //----------------------------------------------------------------------------- 33 | 34 | int init_zynmidirouter(); 35 | int end_zynmidirouter(); 36 | 37 | //MIDI filter initialization 38 | int init_midi_router(); 39 | int end_midi_router(); 40 | 41 | //----------------------------------------------------------------------------- 42 | // Data Structures 43 | //----------------------------------------------------------------------------- 44 | 45 | typedef enum midi_event_type_enum { 46 | //Router-internal pseudo-message codes 47 | CTRL_SWITCH_EVENT=-7, 48 | GATE_OUT_EVENT=-6, 49 | CVGATE_OUT_EVENT=-5, 50 | CVGATE_IN_EVENT=-4, 51 | SWAP_EVENT=-3, 52 | IGNORE_EVENT=-2, 53 | THRU_EVENT=-1, 54 | NONE_EVENT=0, 55 | //Channel 3-bytes-messages 56 | NOTE_OFF=0x8, 57 | NOTE_ON=0x9, 58 | KEY_PRESS=0xA, 59 | CTRL_CHANGE=0xB, 60 | PITCH_BEND=0xE, 61 | //Channel 2-bytes-messages 62 | PROG_CHANGE=0xC, 63 | CHAN_PRESS=0xD, 64 | //System 3-bytes-messages 65 | SONG_POSITION=0xF2, 66 | //System 2-bytes-messages 67 | TIME_CODE_QF=0xF1, 68 | SONG_SELECT=0xF3, 69 | //System 1-byte messages 70 | TUNE_REQUEST=0xF6, 71 | //System Real-Time 72 | TIME_CLOCK=0xF8, 73 | TRANSPORT_START=0xFA, 74 | TRANSPORT_CONTINUE=0xFB, 75 | TRANSPORT_STOP=0xFC, 76 | ACTIVE_SENSE=0xFE, 77 | MIDI_RESET=0xFF, 78 | //System Multi-byte (SysEx) 79 | SYSTEM_EXCLUSIVE=0xF0, 80 | END_SYSTEM_EXCLUSIVE=0xF7 81 | } midi_event_type; 82 | 83 | typedef enum ctrl_mode { 84 | CTRL_MODE_ABS = 0, // Absolute immediate 85 | CTRL_MODE_ABS_JP = 1, // Absolute jump prevention 86 | CTRL_MODE_REL_1 = 2, // Relative 2's complement 87 | CTRL_MODE_REL_2 = 3, // Relative offset 88 | CTRL_MODE_REL_3 = 4, // Relative sign bit 89 | } midi_ctrl_mode; 90 | 91 | typedef struct midi_event_st { 92 | midi_event_type type; 93 | uint8_t chan; 94 | uint8_t num; 95 | uint8_t val; 96 | } midi_event_t; 97 | 98 | //----------------------------------------------------------------------------- 99 | // Global Settings management 100 | //----------------------------------------------------------------------------- 101 | 102 | // Active chain (zmop) management 103 | void set_active_chain(int iz); 104 | int get_active_chain(); 105 | 106 | // Active MIDI chan flag 107 | void set_active_midi_chan(int flag); 108 | int get_active_midi_chan(); 109 | 110 | //Global tuning => Based on MIDI Pitch-Bending messages 111 | void set_tuning_freq(double freq); 112 | int get_tuning_pitchbend(); 113 | 114 | // Master chan management 115 | void set_midi_master_chan(int chan); 116 | int get_midi_master_chan(); 117 | 118 | //Global System Events flag 119 | void set_midi_system_events(int flag); 120 | int get_midi_system_events(); 121 | 122 | //MIDI Learning Mode 123 | void set_midi_learning_mode(int mlm); 124 | int get_midi_learning_mode(); 125 | 126 | //----------------------------------------------------------------------------- 127 | // MIDI Filter 128 | //----------------------------------------------------------------------------- 129 | 130 | typedef struct midi_filter_st { 131 | midi_event_t event_map[8][16][128]; 132 | } midi_filter_t; 133 | 134 | //MIDI Filter Core functions 135 | void set_midi_filter_event_map_st(midi_event_t *ev_from, midi_event_t *ev_to); 136 | void set_midi_filter_event_map(midi_event_type type_from, uint8_t chan_from, uint8_t num_from, midi_event_type type_to, uint8_t chan_to, uint8_t num_to); 137 | void set_midi_filter_event_ignore_st(midi_event_t *ev_from); 138 | void set_midi_filter_event_ignore(midi_event_type type_from, uint8_t chan_from, uint8_t num_from); 139 | midi_event_t *get_midi_filter_event_map_st(midi_event_t *ev_from); 140 | midi_event_t *get_midi_filter_event_map(midi_event_type type_from, uint8_t chan_from, uint8_t num_from); 141 | void del_midi_filter_event_map_st(midi_event_t *ev_filter); 142 | void del_midi_filter_event_map(midi_event_type type_from, uint8_t chan_from, uint8_t num_from); 143 | void reset_midi_filter_event_map(); 144 | 145 | //MIDI Filter Mapping 146 | void set_midi_filter_cc_map(uint8_t chan_from, uint8_t cc_from, uint8_t chan_to, uint8_t cc_to); 147 | void set_midi_filter_cc_ignore(uint8_t chan, uint8_t cc_from); 148 | uint8_t get_midi_filter_cc_map(uint8_t chan, uint8_t cc_from); 149 | void del_midi_filter_cc_map(uint8_t chan, uint8_t cc_from); 150 | void reset_midi_filter_cc_map(); 151 | 152 | //----------------------------------------------------------------------------- 153 | // MIDI Input Ports (ZMIPs) 154 | //----------------------------------------------------------------------------- 155 | 156 | #define ZMIP_DEV0 0 157 | #define ZMIP_DEV1 1 158 | #define ZMIP_DEV2 2 159 | #define ZMIP_DEV3 3 160 | #define ZMIP_DEV4 4 161 | #define ZMIP_DEV5 5 162 | #define ZMIP_DEV6 6 163 | #define ZMIP_DEV7 7 164 | #define ZMIP_DEV8 8 165 | #define ZMIP_DEV9 9 166 | #define ZMIP_DEV10 10 167 | #define ZMIP_DEV11 11 168 | #define ZMIP_DEV12 12 169 | #define ZMIP_DEV13 13 170 | #define ZMIP_DEV14 14 171 | #define ZMIP_DEV15 15 172 | #define ZMIP_DEV16 16 173 | #define ZMIP_DEV17 17 174 | #define ZMIP_DEV18 18 175 | #define ZMIP_DEV19 19 176 | #define ZMIP_DEV20 20 177 | #define ZMIP_DEV21 21 178 | #define ZMIP_DEV22 22 179 | #define ZMIP_DEV23 23 180 | #define ZMIP_SEQ 24 // MIDI from SMF player 181 | #define ZMIP_STEP 25 // MIDI from StepSeq 182 | #define ZMIP_FAKE_INT 26 // BUFFER: Internal MIDI (to ALL zmops => MUST BE CHANGED!!) => Used by zyncoder, zynaptik (CV/Gate), zyntof, etc. 183 | #define ZMIP_CTRL 27 // Engine's controller feedback (setBfree, others?) => It's hardcoded in chain_manager. Update if this number changes!! 184 | #define ZMIP_FAKE_UI 28 // BUFFER: MIDI from UI (to Chain zmops) 185 | #define MAX_NUM_ZMIPS 29 186 | #define NUM_ZMIP_DEVS 24 187 | 188 | #define FLAG_ZMIP_UI 1 189 | #define FLAG_ZMIP_FILTER 1 190 | #define FLAG_ZMIP_ACTIVE_CHAIN 4 191 | #define FLAG_ZMIP_DIRECTIN 8 192 | 193 | #define ZMIP_DEV_FLAGS (FLAG_ZMIP_UI|FLAG_ZMIP_FILTER|FLAG_ZMIP_ACTIVE_CHAIN) 194 | #define ZMIP_SEQ_FLAGS (FLAG_ZMIP_UI) 195 | #define ZMIP_STEP_FLAGS (FLAG_ZMIP_UI|FLAG_ZMIP_FILTER) 196 | #define ZMIP_CTRL_FLAGS (FLAG_ZMIP_UI) 197 | #define ZMIP_INT_FLAGS (FLAG_ZMIP_UI|FLAG_ZMIP_FILTER|FLAG_ZMIP_DIRECTIN) 198 | #define ZMIP_UI_FLAGS (FLAG_ZMIP_DIRECTIN) 199 | 200 | // Structure describing a MIDI input 201 | struct zmip_st { 202 | jack_port_t *jport; // jack midi port 203 | void * buffer; // Pointer to the jack midi buffer 204 | jack_ringbuffer_t * rbuffer; // Direct input ring buffer => Used when DIRECTIN flag is set 205 | 206 | uint32_t flags; // Bitwise flags influencing input behaviour 207 | 208 | uint32_t event_count; // Quantity of events in input event queue (not fake queues) 209 | uint32_t next_event; // Index of the next event to be processed (not fake queues) 210 | jack_midi_event_t event; // Event currently being processed 211 | 212 | uint8_t last_ctrl_val[16][128]; // Last CC value tracked for each CC x 16 chans 213 | }; 214 | 215 | // MIDI Input port (ZMIPs) management 216 | int zmip_init(int iz, char *name, uint32_t flags); 217 | int zmip_end(int iz); 218 | int zmip_get_num_devs(); 219 | int zmip_get_seq_index(); 220 | int zmip_get_step_index(); 221 | int zmip_get_int_index(); 222 | int zmip_get_ctrl_index(); 223 | 224 | // Flag management 225 | int zmip_set_flags(int iz, uint32_t flags); 226 | uint32_t zmip_get_flags(int iz); 227 | int zmip_has_flags(int iz, uint32_t flag); 228 | int zmip_set_flag_active_chain(int iz, uint8_t flag); 229 | int zmip_get_flag_active_chain(int iz); 230 | 231 | // Routing 232 | int zmip_set_route_chains(int iz, int route); // Route/un-route a MIDI input port (zmip) to/from *ALL* zmop chains 233 | 234 | //----------------------------------------------------------------------------- 235 | // MIDI Output Ports (ZMOPs) 236 | //----------------------------------------------------------------------------- 237 | 238 | #define ZMOP_CH0 0 239 | #define ZMOP_CH1 1 240 | #define ZMOP_CH2 2 241 | #define ZMOP_CH3 3 242 | #define ZMOP_CH4 4 243 | #define ZMOP_CH5 5 244 | #define ZMOP_CH6 6 245 | #define ZMOP_CH7 7 246 | #define ZMOP_CH8 8 247 | #define ZMOP_CH9 9 248 | #define ZMOP_CH10 10 249 | #define ZMOP_CH11 11 250 | #define ZMOP_CH12 12 251 | #define ZMOP_CH13 13 252 | #define ZMOP_CH14 14 253 | #define ZMOP_CH15 15 254 | #define ZMOP_MOD 16 // MOD-UI 255 | #define ZMOP_STEP 17 // StepSeq 256 | #define ZMOP_CTRL 18 // Controller Feedback => To replace! 257 | #define ZMOP_DEV0 19 258 | #define ZMOP_DEV1 20 259 | #define ZMOP_DEV2 21 260 | #define ZMOP_DEV3 22 261 | #define ZMOP_DEV4 23 262 | #define ZMOP_DEV5 24 263 | #define ZMOP_DEV6 25 264 | #define ZMOP_DEV7 26 265 | #define ZMOP_DEV8 27 266 | #define ZMOP_DEV9 28 267 | #define ZMOP_DEV10 29 268 | #define ZMOP_DEV11 30 269 | #define ZMOP_DEV12 31 270 | #define ZMOP_DEV13 32 271 | #define ZMOP_DEV14 33 272 | #define ZMOP_DEV15 34 273 | #define ZMOP_DEV16 35 274 | #define ZMOP_DEV17 36 275 | #define ZMOP_DEV18 37 276 | #define ZMOP_DEV19 38 277 | #define ZMOP_DEV20 39 278 | #define ZMOP_DEV21 40 279 | #define ZMOP_DEV22 41 280 | #define ZMOP_DEV23 42 281 | #define MAX_NUM_ZMOPS 43 282 | #define NUM_ZMOP_CHAINS 17 // Do not exceed 32 - use uint32_t bitwise flags for chain flags 283 | #define NUM_ZMOP_DEVS 24 284 | 285 | #define FLAG_ZMOP_DROPPC 1 286 | #define FLAG_ZMOP_DROPCC 2 287 | #define FLAG_ZMOP_DROPSYS 4 288 | #define FLAG_ZMOP_DROPSYSEX 8 289 | #define FLAG_ZMOP_DROPNOTE 16 290 | #define FLAG_ZMOP_TUNING 32 291 | #define FLAG_ZMOP_NOTERANGE 64 292 | #define FLAG_ZMOP_CHAN_TRANSFILTER 128 293 | #define FLAG_ZMOP_DIRECTOUT 256 294 | 295 | //#define ZMOP_CHAIN_FLAGS (FLAG_ZMOP_TUNING|FLAG_ZMOP_NOTERANGE|FLAG_ZMOP_DROPSYS|FLAG_ZMOP_DROPSYSEX|FLAG_ZMOP_CHAN_TRANSFILTER|FLAG_ZMOP_DIRECTOUT) 296 | #define ZMOP_CHAIN_FLAGS (FLAG_ZMOP_TUNING|FLAG_ZMOP_NOTERANGE|FLAG_ZMOP_DROPSYSEX|FLAG_ZMOP_CHAN_TRANSFILTER|FLAG_ZMOP_DIRECTOUT) 297 | 298 | // Structure describing a MIDI output 299 | struct zmop_st { 300 | jack_port_t *jport; // jack midi port 301 | void * buffer; // pointer to jack midi output buffer 302 | jack_ringbuffer_t * rbuffer; // direct output ring buffer (optional) 303 | 304 | int midi_chan; // Single MIDI channel. -1 for using channel translation map only. 305 | int midi_chans[16]; // MIDI channel translation map (-1 to disable a MIDI channel) 306 | int route_from_zmips[MAX_NUM_ZMIPS]; // Flags indicating which inputs to route to this output 307 | uint8_t cc_route[128]; // CCs routed to output (0 = blocked, 1 = routed) 308 | uint32_t flags; // Bitwise flags influencing output behaviour 309 | uint8_t note_low; // Note range => Low note 310 | uint8_t note_high; // Note range => High note 311 | int8_t transpose_octave; // Transpose coarse => octave 312 | int8_t transpose_semitone; // Transpose fine => semitone 313 | 314 | uint8_t note_state[128]; // Note state array for managing pressed notes across active chain changes. 315 | int8_t note_transpose[128]; // Note transpose array for managing pressed notes across transpose changes. 316 | uint16_t last_pb_val[16]; // Last pitch-bending value. Do we need multi-channel tracking for MPE? 317 | 318 | int n_connections; // Quantity of jack connections (used for optimisation) 319 | }; 320 | 321 | // MIDI output port (ZMOPs) management 322 | int zmop_init(int iz, char *name, uint32_t flags); 323 | int zmop_end(int iz); 324 | int zmop_get_num_chains(); 325 | int zmop_get_num_devs(); 326 | // Flag management 327 | int zmop_set_flags(int iz, uint32_t flags); 328 | uint32_t zmop_get_flags(int iz); 329 | int zmop_has_flags(int iz, uint32_t flag); 330 | int zmop_set_flag_droppc(int iz, uint8_t flag); 331 | int zmop_get_flag_droppc(int iz); 332 | int zmop_set_flag_dropcc(int iz, uint8_t flag); 333 | int zmop_get_flag_dropcc(int iz); 334 | int zmop_set_flag_dropsys(int iz, uint8_t flag); 335 | int zmop_get_flag_dropsys(int iz); 336 | int zmop_set_flag_dropsysex(int iz, uint8_t flag); 337 | int zmop_get_flag_dropsysex(int iz); 338 | int zmop_set_flag_dropnote(int iz, uint8_t flag); 339 | int zmop_get_flag_dropnote(int iz); 340 | int zmop_set_flag_tuning(int iz, uint8_t flag); 341 | int zmop_get_flag_tuning(int iz); 342 | int zmop_set_flag_chan_transfilter(int iz, uint8_t flag); 343 | int zmop_get_flag_chan_transfilter(int iz); 344 | // MIDI channel management 345 | int zmop_reset_midi_chans(int iz); 346 | int zmop_set_midi_chan(int iz, int midi_chan); 347 | int zmop_set_midi_chan_trans(int iz, int midi_chan, int midi_chan_trans); 348 | int zmop_set_midi_chan_all(int iz); 349 | int zmop_set_midi_chan_all_trans(int iz, int midi_chan); 350 | int zmop_set_midi_chan_to(int iz, int midi_chan_from, int midi_chan_to); 351 | int zmop_get_midi_chan_to(int iz, int midi_chan_from); 352 | int zmop_get_midi_chan_info(int iz, int *buffer); 353 | // Routing zmip => zmop 354 | int zmop_reset_routes_from(int izmop); 355 | int zmop_set_route_from(int izmop, int izmip, int route); 356 | int zmop_get_route_from(int izmop, int izmip); 357 | int zmop_get_routes_info(int izmop, int *buffer); 358 | int zmop_get_routes_info_all(int *buffer); 359 | // MIDI Note Range & Transpose 360 | int zmop_set_note_low(int iz, uint8_t nlow); 361 | int zmop_set_note_high(int iz, uint8_t nhigh); 362 | int zmop_set_transpose_octave(int iz, int8_t trans_oct); 363 | int zmop_set_transpose_semitone(int iz, int8_t trans_semi); 364 | int set_global_transpose(int8_t transpose); 365 | uint8_t zmop_get_note_low(int iz); 366 | uint8_t zmop_get_note_high(int iz); 367 | int8_t zmop_get_transpose_octave(int iz); 368 | int8_t zmop_get_transpose_semitone(int iz); 369 | int8_t get_global_transpose(); 370 | int zmop_set_note_range_transpose(int iz, uint8_t nlow, uint8_t nhigh, int8_t trans_oct, int8_t trans_semi); 371 | int zmop_reset_note_range_transpose(int iz); 372 | // CC routing 373 | int zmop_reset_cc_route(int iz); 374 | int zmop_set_cc_route(int iz, uint8_t *cc_route); 375 | int zmop_get_cc_route(int iz, uint8_t *cc_route); 376 | // ---------------------------------------------------------------------------- 377 | // This is called from jack process!! 378 | void zmop_push_event(struct zmop_st * zmop, jack_midi_event_t * ev); // Add event to MIDI output port 379 | 380 | //----------------------------------------------------------------------------- 381 | // Pedal management 382 | //----------------------------------------------------------------------------- 383 | 384 | static const uint8_t pedal_cc[] = {64, 66, 67, 69}; // List of CC that should be treated as pedals 385 | 386 | uint32_t get_cc_pedal(uint8_t pedal); 387 | int clear_cc_pedals(uint8_t izmip); 388 | 389 | //----------------------------------------------------------------------------- 390 | // Jack MIDI Process 391 | //----------------------------------------------------------------------------- 392 | 393 | int init_jack_midi(char *name); 394 | int end_jack_midi(); 395 | void populate_midi_event_from_rb(jack_ringbuffer_t *rb, jack_midi_event_t *event); 396 | void populate_zmip_event(struct zmip_st * zmip); 397 | int jack_process(jack_nframes_t nframes, void *arg); 398 | int jack_buffer_size_change(jack_nframes_t nframes, void *arg); 399 | void jack_connect_cb(jack_port_id_t a, jack_port_id_t b, int connect, void *arg); 400 | 401 | //----------------------------------------------------------------------------- 402 | // MIDI Events Buffer Management and Direct Send functions 403 | //----------------------------------------------------------------------------- 404 | 405 | #define JACK_MIDI_BUFFER_SIZE 16384 406 | 407 | // Direct Send Event Ring-Buffer write 408 | int write_rb_midi_event(jack_ringbuffer_t *rb, uint8_t *event_buffer, int event_size); 409 | 410 | // ZMIP Direct Send Functions 411 | int zmip_send_midi_event(uint8_t iz, uint8_t *event_buffer, int event_size); 412 | int zmip_send_note_off(uint8_t iz, uint8_t chan, uint8_t note, uint8_t vel); 413 | int zmip_send_note_on(uint8_t iz, uint8_t chan, uint8_t note, uint8_t vel); 414 | int zmip_send_ccontrol_change(uint8_t iz, uint8_t chan, uint8_t ctrl, uint8_t val); 415 | int zmip_send_master_ccontrol_change(uint8_t iz, uint8_t ctrl, uint8_t val); 416 | int zmip_send_program_change(uint8_t iz, uint8_t chan, uint8_t prgm); 417 | int zmip_send_chan_press(uint8_t iz, uint8_t chan, uint8_t val); 418 | int zmip_send_pitchbend_change(uint8_t iz, uint8_t chan, uint16_t pb); 419 | int zmip_send_all_notes_off(uint8_t iz); 420 | int zmip_send_all_notes_off_chan(uint8_t iz, uint8_t izmop); 421 | 422 | // ZMIP_FAKE_UI Direct Send Functions 423 | int ui_send_midi_event(uint8_t *event_buffer, int event_size); 424 | int ui_send_note_off(uint8_t chan, uint8_t note, uint8_t vel); 425 | int ui_send_note_on(uint8_t chan, uint8_t note, uint8_t vel); 426 | int ui_send_ccontrol_change(uint8_t chan, uint8_t ctrl, uint8_t val); 427 | int ui_send_master_ccontrol_change(uint8_t ctrl, uint8_t val); 428 | int ui_send_program_change(uint8_t chan, uint8_t prgm); 429 | int ui_send_chan_press(uint8_t chan, uint8_t val); 430 | int ui_send_pitchbend_change(uint8_t chan, uint16_t pb); 431 | int ui_send_all_notes_off(); 432 | int ui_send_all_notes_off_chan(uint8_t izmop); 433 | 434 | // ZMOP Direct Send Functions 435 | int zmop_send_midi_event(uint8_t iz, uint8_t *event_buffer, int event_size); 436 | int zmop_send_note_off(uint8_t iz, uint8_t chan, uint8_t note, uint8_t vel); 437 | int zmop_send_note_on(uint8_t iz, uint8_t chan, uint8_t note, uint8_t vel); 438 | int zmop_send_ccontrol_change(uint8_t iz, uint8_t chan, uint8_t ctrl, uint8_t val); 439 | int zmop_send_program_change(uint8_t iz, uint8_t chan, uint8_t prgm); 440 | int zmop_send_chan_press(uint8_t iz, uint8_t chan, uint8_t val); 441 | int zmop_send_pitchbend_change(uint8_t iz, uint8_t chan, uint16_t pb); 442 | 443 | // ZMOP_CTRL Direct Send Functions 444 | int ctrlfb_send_midi_event(uint8_t *event_buffer, int event_size); 445 | int ctrlfb_send_note_off(uint8_t chan, uint8_t note, uint8_t vel); 446 | int ctrlfb_send_note_on(uint8_t chan, uint8_t note, uint8_t vel); 447 | int ctrlfb_send_ccontrol_change(uint8_t chan, uint8_t ctrl, uint8_t val); 448 | int ctrlfb_send_program_change(uint8_t chan, uint8_t prgm); 449 | 450 | // ZMOP_DEV Direct Send Functions 451 | int dev_send_midi_event(uint8_t idev, uint8_t *event_buffer, int event_size); 452 | int dev_send_note_off(uint8_t idev, uint8_t chan, uint8_t note, uint8_t vel); 453 | int dev_send_note_on(uint8_t idev, uint8_t chan, uint8_t note, uint8_t vel); 454 | int dev_send_ccontrol_change(uint8_t idev, uint8_t chan, uint8_t ctrl, uint8_t val); 455 | int dev_send_program_change(uint8_t idev, uint8_t chan, uint8_t prgm); 456 | 457 | //----------------------------------------------------------------------------- 458 | // MIDI Internal Ouput Events Buffer => UI 459 | //----------------------------------------------------------------------------- 460 | 461 | // Size in bytes. Each message is 4-bytes long (uint32_t) 462 | #define ZYNMIDI_BUFFER_SIZE 16384 463 | 464 | int init_zynmidi_buffer(); 465 | int end_zynmidi_buffer(); 466 | int write_zynmidi(uint32_t ev); 467 | uint32_t read_zynmidi(); 468 | int read_zynmidi_buffer(uint32_t *buffer, int n); 469 | int get_zynmidi_num_max(); 470 | int get_zynmidi_num_pending(); 471 | 472 | int write_zynmidi_note_off(uint8_t chan, uint8_t num, uint8_t val); 473 | int write_zynmidi_note_on(uint8_t chan, uint8_t num, uint8_t val); 474 | int write_zynmidi_ccontrol_change(uint8_t chan, uint8_t num, uint8_t val); 475 | int write_zynmidi_program_change(uint8_t chan, uint8_t num); 476 | 477 | //----------------------------------------------------------------------------- 478 | -------------------------------------------------------------------------------- /zynmidirouter.txt: -------------------------------------------------------------------------------- 1 | zynmidirouter description 2 | ========================= 3 | 4 | zynmidirouter is a core module. It provides routing, filtering and translation of MIDI messages. 5 | 6 | JACK MIDI input ports 7 | ===================== 8 | 9 | dev0..dev15: For physical MIDI port connections, one per physical port - also TouchOSC 10 | net: Connection from MIDI over network protocols, e.g. QmidiNet, jackrtpmidi (not RtMidiOut (TouchOSC)) 11 | Q. Why not present all network protocols as dev inputs? 12 | seq: For Standard MIDI File (SMF) player 13 | step: For step sequencer 14 | ctrl: Control feedback from engines (i.e. setBfree tonewheel faders) 15 | Q. Why is this required? 16 | 17 | JACK MIDI output ports 18 | ====================== 19 | ch0..ch15: Connection to each chain 20 | main: MIDI recorder, engines not in chains 21 | midi: Hardware MIDI outputs + ZynMaster (MIDI/CV+Gate) 22 | net: MIDI over network protocols, e.g. QmidiNet, rtpmidi 23 | ctrl: MIDI outputs configured as feedback ports 24 | step: Connection to step sequencer 25 | 26 | Virtual MIDI inputs 27 | =================== 28 | int: Internal MIDI messages 29 | Q. What are these? 30 | ui: Messages triggered by user interface 31 | Q. Like what? 32 | ctrl_fb: 33 | Q. What is ctrl_fb used for? 34 | 35 | Virtual MIDI outputs 36 | ==================== 37 | zynmidi: Messages targetted at user interface and CC learn (zyngui reads this queue. All CC messages going to chains) 38 | 39 | Note: Virtual input and output queues are normalised and limited to 3 byte messages. They are not scheduled within the jack frame. All messages are despatched at start of frame. 40 | 41 | Input processing 42 | ================ 43 | 44 | Each MIDI message received on a device input (mostly physical / external) is processed in the order they are received. Messages from virtual sources are processed and despatched first. After each mesage is processed it is routed to the appropriate outputs and duplicated as necessary based on rules such as channel cloning. 45 | 46 | The input processing stage performs these processes: 47 | 48 | - Drop Active Sense and SysEx messages 49 | - Drop system messages if configured (global) 50 | - Transform to active channel if stage mode enabled (dev, net & smf inputs) 51 | Q. Do we want to transform SMF to active channel? 52 | - Capture CC, Note-on, Note-off message for UI before further processing (only if MIDI learn enabled) 53 | - Apply MIDI filter according to user configured rules (dev, net, seq): 54 | - Drop ignored events 55 | - Map prog change, channel pressure, pitch bend, CC, etc. 56 | Q. This maps MIDI channel - does this break stage mode? 57 | - Send "Master Channel" message to UI (then drop message, master channel messages are only sent to UI) 58 | - Send program change message to UI 59 | - Process CC for absolute / relative modes 60 | Q. Check this works as expected and implement other modes 61 | - Store CC value to facilitate change of channel in stage mode 62 | - Store note on/off value to facilitate change of channel in stage mode and all notes off function 63 | - Capture message for UI after processing (only if not already captured before processing and: note on/off, CC or system message) 64 | - Despatch message to UI (if captured) 65 | - Map CC (swap CC number as defined in input filter) 66 | 67 | Output processing 68 | ================= 69 | 70 | After each MIDI event is processed by the input filtering and translation stage it is sent to the outputs defined by configuration rules. The following processing is applied to each output: 71 | 72 | - Drop message if output is not connected 73 | - Drop program change message if configured in rule (except from UI) 74 | - Drop CC targetted at chains unless from internal (fake) source 75 | - Drop if source / destination route not configured 76 | - Drop if wrong MIDI channel 77 | - Send to post-output processing 78 | - Clone to additional outputs as configured 79 | 80 | Post-Output processing 81 | ====================== 82 | 83 | Before actually sending a message to an output, further processing is performed: 84 | 85 | - Filter note range 86 | - Transpose 87 | - Filter on MIDI channel 88 | - Filter on source / destination (routing) 89 | - Translate MIDI channel 90 | - Addition of pitchbend message if fine tuning enabled (currently a global configuration but could be per output) 91 | -------------------------------------------------------------------------------- /zynpot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zynpot, wrapper library for rotaries 4 | * 5 | * Library for interfacing rotaries of several types 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "zynpot.h" 31 | #include "zyncoder.h" 32 | #include "zynrv112.h" 33 | 34 | //----------------------------------------------------------------------------- 35 | // Global variables 36 | //----------------------------------------------------------------------------- 37 | 38 | extern zyncoder_t zyncoders[MAX_NUM_ZYNCODERS]; 39 | extern rv112_t rv112s[MAX_NUM_RV112]; 40 | 41 | zynpot_t zynpots[MAX_NUM_ZYNPOTS]; 42 | void (*zynpot_cb)(int8_t, int32_t); 43 | 44 | //----------------------------------------------------------------------------- 45 | // Zynpot common API 46 | //----------------------------------------------------------------------------- 47 | 48 | void reset_zynpots() { 49 | int i; 50 | for (i=0;iMAX_NUM_ZYNPOTS) { 72 | fprintf(stderr, "ZynCore->setup_zynpot(%d): Invalid index!\n", i); 73 | return 0; 74 | } 75 | zynpots[i].type = type; 76 | zynpots[i].i = ii; 77 | switch (type) { 78 | case ZYNPOT_ZYNCODER: 79 | zyncoders[ii].zpot_i = (int8_t)i; 80 | zynpots[i].data = (zynpot_data_t *) &zyncoders[ii]; 81 | zynpots[i].setup_behaviour = setup_behaviour_zyncoder; 82 | zynpots[i].get_value = get_value_zyncoder; 83 | break; 84 | case ZYNPOT_RV112: 85 | rv112s[ii].zpot_i = (int8_t)i; 86 | zynpots[i].data = (zynpot_data_t *) &rv112s[ii]; 87 | zynpots[i].setup_behaviour = setup_behaviour_rv112; 88 | zynpots[i].get_value = get_value_rv112; 89 | break; 90 | } 91 | return 1; 92 | } 93 | 94 | int setup_behaviour_zynpot(uint8_t i, int32_t step) { 95 | if (i>MAX_NUM_ZYNPOTS || zynpots[i].type==ZYNPOT_NONE) { 96 | fprintf(stderr, "ZynCore->setup_behaviour_zynpot(%d): Invalid index!\n", i); 97 | return 0; 98 | } 99 | return zynpots[i].setup_behaviour(zynpots[i].i, step); 100 | } 101 | 102 | int32_t get_value_zynpot(uint8_t i) { 103 | if (i>=MAX_NUM_ZYNPOTS || zynpots[i].type==ZYNPOT_NONE) { 104 | fprintf(stderr, "ZynCore->get_value_zynpot(%d): Invalid index!\n", i); 105 | return 0; 106 | } 107 | return zynpots[i].get_value(zynpots[i].i); 108 | } 109 | 110 | //----------------------------------------------------------------------------- 111 | -------------------------------------------------------------------------------- /zynpot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: Zynpot, wrapper library for rotaries 4 | * 5 | * Library for interfacing rotaries of several types 6 | * 7 | * Copyright (C) 2015-2021 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | 28 | //----------------------------------------------------------------------------- 29 | // Zynpot data 30 | //----------------------------------------------------------------------------- 31 | 32 | #define ZYNPOT_NONE 0 33 | #define ZYNPOT_ZYNCODER 1 34 | #define ZYNPOT_RV112 2 35 | 36 | #define MAX_NUM_ZYNPOTS 4 37 | 38 | typedef struct zynpot_data_st { 39 | uint8_t enabled; 40 | int32_t step; 41 | int32_t value; 42 | int8_t zpot_i; 43 | } zynpot_data_t; 44 | 45 | 46 | typedef struct zynpot_st { 47 | uint8_t type; 48 | uint8_t i; 49 | zynpot_data_t *data; 50 | 51 | // Function pointers 52 | int (*setup_behaviour)(uint8_t, int32_t); 53 | int32_t (*get_value)(uint8_t); 54 | } zynpot_t; 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | //----------------------------------------------------------------------------- 61 | // Zynpot common API 62 | //----------------------------------------------------------------------------- 63 | 64 | /** 65 | * Reset all zynpots 66 | * @see setup_zynpot() 67 | */ 68 | void reset_zynpots(); 69 | 70 | /** 71 | * Get the number of configured zynpots 72 | * @return number of zynpots bond to a zyncoder or rv112 object 73 | * @see setup_zynpot() 74 | */ 75 | int get_num_zynpots(); 76 | 77 | /** 78 | * Set the callback function for zynpots 79 | * @param i index of zynpot to setup 80 | * @param cbfunc function being called when zynpot's value changes 81 | */ 82 | void setup_zynpot_cb(void (*cbfunc)(int8_t, int32_t)); 83 | 84 | /** 85 | * Initialize a zynpot object, binding it to a zyncoder or rv112 object 86 | * @param i index of zynpot to initialize 87 | * @param type type of zynpot (ZYNPOT_NONE, ZYNPOT_ZYNCODER | ZYNPOT_RV112) 88 | * @param ii index of zyncoder/rv112 to bind to 89 | * @return 1 => OK, 0 => Error 90 | * @see zyncoder.h 91 | * @see zynrv112.h 92 | */ 93 | int setup_zynpot(uint8_t i, uint8_t type, uint8_t ii); 94 | 95 | /** 96 | * Setup the behaviour of a zynpot object 97 | * @param i zynpot index 98 | * @param step 0 => dynamic step, >=1 => step size 99 | * @return 1 => OK, 0 => Error 100 | */ 101 | int setup_behaviour_zynpot(uint8_t i, int32_t step); 102 | 103 | /** 104 | * Get the value of a zynpot object 105 | * It shouldn't be used if a CB function has been setup 106 | * @param i zynpot index 107 | * @param step 0 => dynamic step, >=1 => step size 108 | * @return the accumulated value from the last call 109 | */ 110 | int32_t get_value_zynpot(uint8_t i); 111 | 112 | 113 | //----------------------------------------------------------------------------- 114 | 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | 119 | -------------------------------------------------------------------------------- /zynrv112.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ****************************************************************** 4 | * ZYNTHIAN PROJECT: RV112 + ADS1115 5 | * 6 | * Library for reading RV112 infinite potentiometer using ADS1115. 7 | * 8 | * Copyright (C) 2015-2021 Fernando Moyano 9 | * 10 | * ****************************************************************** 11 | * 12 | * This program is free software; you can redistribute it and/or 13 | * modify it under the terms of the GNU General Public License as 14 | * published by the Free Software Foundation; either version 2 of 15 | * the License, or any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 23 | * 24 | * ****************************************************************** 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "zynads1115.h" 39 | #include "zynpot.h" 40 | #include "zynrv112.h" 41 | 42 | //----------------------------------------------------------------------------- 43 | // Global variables 44 | //----------------------------------------------------------------------------- 45 | 46 | rv112_t rv112s[MAX_NUM_RV112]; 47 | 48 | //----------------------------------------------------------------------------- 49 | 50 | extern void (*zynpot_cb)(int8_t, int32_t); 51 | 52 | //----------------------------------------------------------------------------- 53 | // RV112's zynpot API 54 | //----------------------------------------------------------------------------- 55 | 56 | void init_rv112s() { 57 | int i,j; 58 | boost::circular_buffer *dvbuf; 59 | for (i=0;i(DVBUF_SIZE); 67 | for (j=0;jpush_back(0); 68 | } 69 | } 70 | 71 | void end_rv112s() { 72 | int i; 73 | for (i=0;i *)rv112s[i].dvbuf; 81 | rv112s[i].dvbuf = NULL; 82 | } 83 | } 84 | 85 | int get_num_rv112s() { 86 | int i; 87 | int n = 0; 88 | for (i=0;i MAX_NUM_RV112) { 96 | fprintf(stderr, "ZynCore->setup_rv112(%d): Invalid index!\n", i); 97 | return 0; 98 | } 99 | uint8_t pos = (i % 2) * 2; 100 | rv112s[i].ads1115_node = ads1115; 101 | if (reversed_chans == 0) { 102 | rv112s[i].chA = pos + 1; 103 | rv112s[i].chB = pos; 104 | } else { 105 | rv112s[i].chA = pos; 106 | rv112s[i].chB = pos + 1; 107 | } 108 | rv112s[i].valA = ads1115_analog_read(ads1115, rv112s[i].chA); 109 | rv112s[i].valB = ads1115_analog_read(ads1115, rv112s[i].chB); 110 | rv112s[i].curseg = 0; 111 | rv112s[i].lastdv = 0; 112 | rv112s[i].value = 0; 113 | rv112s[i].step = 1; 114 | rv112s[i].valraw = 0; 115 | rv112s[i].enabled = 1; 116 | return 1; 117 | } 118 | 119 | int setup_behaviour_rv112(uint8_t i, int32_t step) { 120 | if (i>=MAX_NUM_RV112 || rv112s[i].enabled==0) { 121 | fprintf(stderr, "ZynCore->setup_step_rv112(%d, ...): Invalid index!\n", i); 122 | return 0; 123 | } 124 | 125 | rv112s[i].step = step; 126 | rv112s[i].valraw = 0; 127 | rv112s[i].value = 0; 128 | 129 | return 1; 130 | } 131 | 132 | int32_t get_value_rv112(uint8_t i) { 133 | if (i>=MAX_NUM_RV112 || rv112s[i].enabled==0) { 134 | fprintf(stderr, "ZynCore->get_value_rv112(%d): Invalid index!\n", i); 135 | return 0; 136 | } 137 | int32_t res = rv112s[i].value; 138 | if (res!=0) { 139 | rv112s[i].valraw = 0; 140 | rv112s[i].value = 0; 141 | } 142 | return res; 143 | } 144 | 145 | //----------------------------------------------------------------------------- 146 | // RV112 specific functions 147 | //----------------------------------------------------------------------------- 148 | 149 | int16_t read_rv112(uint8_t i) { 150 | int32_t vA = ads1115_analog_read(rv112s[i].ads1115_node, rv112s[i].chA); 151 | int32_t vB = ads1115_analog_read(rv112s[i].ads1115_node, rv112s[i].chB); 152 | int16_t d = 0; 153 | 154 | switch (rv112s[i].curseg) { 155 | case 0: 156 | if (vB < RV112_ADS1115_RANGE_25) { 157 | d = rv112s[i].valA - vA; 158 | break; 159 | } 160 | else if (vA > RV112_ADS1115_RANGE_75) { 161 | d = rv112s[i].valB - vB; 162 | rv112s[i].curseg = 1; 163 | break; 164 | } 165 | else if (vA < RV112_ADS1115_RANGE_25) { 166 | d = vB - rv112s[i].valB; 167 | rv112s[i].curseg = 3; 168 | break; 169 | } 170 | else if (vB > RV112_ADS1115_RANGE_75) { 171 | d = vA - rv112s[i].valA; 172 | rv112s[i].curseg = 2; 173 | break; 174 | } 175 | break; 176 | 177 | case 1: 178 | if (vA > RV112_ADS1115_RANGE_75) { 179 | d = rv112s[i].valB - vB; 180 | break; 181 | } 182 | else if (vB > RV112_ADS1115_RANGE_75) { 183 | d = vA - rv112s[i].valA; 184 | rv112s[i].curseg = 2; 185 | break; 186 | } 187 | else if (vB < RV112_ADS1115_RANGE_25) { 188 | d = rv112s[i].valA - vA; 189 | rv112s[i].curseg = 0; 190 | break; 191 | } 192 | else if (vA < RV112_ADS1115_RANGE_25) { 193 | d = vB - rv112s[i].valB; 194 | rv112s[i].curseg = 3; 195 | break; 196 | } 197 | break; 198 | 199 | case 2: 200 | if (vB > RV112_ADS1115_RANGE_75) { 201 | d = vA - rv112s[i].valA; 202 | break; 203 | } 204 | else if (vA < RV112_ADS1115_RANGE_25) { 205 | d = vB - rv112s[i].valB; 206 | rv112s[i].curseg = 3; 207 | break; 208 | } 209 | else if (vA > RV112_ADS1115_RANGE_75) { 210 | d = rv112s[i].valB - vB; 211 | rv112s[i].curseg = 1; 212 | break; 213 | } 214 | else if (vB < RV112_ADS1115_RANGE_25) { 215 | d = rv112s[i].valA - vA; 216 | rv112s[i].curseg = 0; 217 | break; 218 | } 219 | break; 220 | 221 | case 3: 222 | if (vA < RV112_ADS1115_RANGE_25) { 223 | d = vB - rv112s[i].valB; 224 | break; 225 | } 226 | else if (vB < RV112_ADS1115_RANGE_25) { 227 | d = rv112s[i].valA - vA; 228 | rv112s[i].curseg = 0; 229 | break; 230 | } 231 | else if (vB > RV112_ADS1115_RANGE_75) { 232 | d = vA - rv112s[i].valA; 233 | rv112s[i].curseg = 2; 234 | break; 235 | } 236 | else if (vA > RV112_ADS1115_RANGE_75) { 237 | d = rv112s[i].valB - vB; 238 | rv112s[i].curseg = 1; 239 | break; 240 | } 241 | break; 242 | } 243 | 244 | rv112s[i].valA = vA; 245 | rv112s[i].valB = vB; 246 | //fprintf(stderr, "RV112[%d]: curseg = %d, vA = %d, vB = %d, d = %d\n", i, rv112s[i].curseg, vA, vB, d); 247 | return d / RV112_ADS1115_NOISE_DIV; 248 | } 249 | 250 | void * poll_rv112(void *arg) { 251 | int i = 0; 252 | int j = 0; 253 | int32_t vr, v; 254 | boost::circular_buffer *dvbuf; 255 | while (1) { 256 | if (rv112s[i].enabled) { 257 | rv112s[i].lastdv = read_rv112(i); 258 | // Calculate moving average for adaptative speed variation 259 | if (rv112s[i].step==0) { 260 | dvbuf = (boost::circular_buffer *)rv112s[i].dvbuf; 261 | dvbuf->push_back(abs(rv112s[i].lastdv)); 262 | rv112s[i].dvavg += (*dvbuf)[DVBUF_SIZE-1] - (*dvbuf)[0]; 263 | //fprintf(stderr, "DVAVG %d = %d\n", i, rv112s[i].dvavg); 264 | } 265 | if (rv112s[i].lastdv!=0) { 266 | // Adaptative speed variation using a moving average 267 | if (rv112s[i].step==0) { 268 | if (rv112s[i].dvavg < 1000) rv112s[i].lastdv /= 8; 269 | else if (rv112s[i].dvavg < 2000) rv112s[i].lastdv /= 4; 270 | else if (rv112s[i].dvavg < 4000) rv112s[i].lastdv /= 2; 271 | } 272 | else { 273 | rv112s[i].lastdv /= (8 * rv112s[i].step); 274 | } 275 | vr = rv112s[i].valraw + rv112s[i].lastdv; 276 | 277 | // calculate value & call CB function 278 | if (vr!=rv112s[i].valraw) { 279 | rv112s[i].valraw = vr; 280 | rv112s[i].value = vr / RV112_ADS1115_RAW_DIV; 281 | if (rv112s[i].value!=0 && zynpot_cb) { 282 | //fprintf(stderr, "RV112(%d): Vraw=%d, Value=%d\n", i, vr, rv112s[i].value); 283 | zynpot_cb(rv112s[i].zpot_i, rv112s[i].value); 284 | rv112s[i].valraw = 0; 285 | rv112s[i].value = 0; 286 | } 287 | } 288 | } 289 | } 290 | // Calc next rotary ... 291 | i = (i + 1) % MAX_NUM_RV112; 292 | // prioritize active rotaries, cycling among them, but force a full cyle from time to time ... ... 293 | if (j 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #ifndef ZYNRV112_H 27 | #define ZYNRV112_H 28 | 29 | #include 30 | 31 | #include "zynads1115.h" 32 | 33 | //----------------------------------------------------------------------------- 34 | // ADS1115 data 35 | //----------------------------------------------------------------------------- 36 | 37 | #define ADS1115_VDD 3.3 38 | #define MAX_NUM_ADS1115 2 39 | 40 | //----------------------------------------------------------------------------- 41 | // RV112 data 42 | //----------------------------------------------------------------------------- 43 | 44 | #define RV112_ADS1115_RANGE_100 (int)(0xFFFF*ADS1115_VDD/4.096)/2 45 | #define RV112_ADS1115_RANGE_25 RV112_ADS1115_RANGE_100/4 46 | #define RV112_ADS1115_RANGE_50 RV112_ADS1115_RANGE_100/2 47 | #define RV112_ADS1115_RANGE_75 3*(RV112_ADS1115_RANGE_100/4) 48 | 49 | //#define RV112_ADS1115_NOISE_DIV 32 50 | #define RV112_ADS1115_NOISE_DIV 8 51 | #define RV112_ADS1115_RAW_DIV 20 52 | 53 | #define MAX_NUM_RV112 4 54 | #define DVBUF_SIZE 10 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | typedef struct rv112_st { 61 | uint8_t enabled; 62 | int32_t step; 63 | int32_t value; 64 | int8_t zpot_i; 65 | 66 | // Next fields are RV112-specific 67 | ads1115_t *ads1115_node; 68 | uint16_t chA; 69 | uint16_t chB; 70 | 71 | int32_t valA; 72 | int32_t valB; 73 | uint8_t curseg; 74 | int16_t lastdv; 75 | int32_t valraw; 76 | void *dvbuf; 77 | int32_t dvavg; 78 | } rv112_t; 79 | 80 | //----------------------------------------------------------------------------- 81 | // RV112's zynpot API 82 | //----------------------------------------------------------------------------- 83 | 84 | void init_rv112s(); 85 | void end_rv112s(); 86 | 87 | int get_num_rv112s(); 88 | 89 | int setup_rv112(uint8_t i, ads1115_t *ads1115, uint8_t reversed_chans); 90 | 91 | int setup_behaviour_rv112(uint8_t i, int32_t step); 92 | int32_t get_value_rv112(uint8_t i); 93 | 94 | //----------------------------------------------------------------------------- 95 | // RV112 specific functions 96 | //----------------------------------------------------------------------------- 97 | 98 | int16_t read_rv112(uint8_t i); 99 | pthread_t init_poll_rv112(); 100 | 101 | //----------------------------------------------------------------------------- 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | //----------------------------------------------------------------------------- 108 | 109 | #endif -------------------------------------------------------------------------------- /zyntof.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ****************************************************************** 3 | * ZYNTHIAN PROJECT: TOF Library 4 | * 5 | * Library for interfacing VL53L0X "Time Of Flight" sensor 6 | * 7 | * Copyright (C) 2015-2020 Fernando Moyano 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #include "zynpot.h" 39 | #include "zyncoder.h" 40 | #include "zyntof.h" 41 | 42 | //----------------------------------------------------------------------------- 43 | // Global variables 44 | //----------------------------------------------------------------------------- 45 | 46 | struct zyntof_st zyntofs[MAX_NUM_ZYNTOFS]; 47 | 48 | //----------------------------------------------------------------------------- 49 | // TCA954X (43/44/48) Stuff => I2C Multiplexer 50 | //----------------------------------------------------------------------------- 51 | 52 | int i2cmult_fd = 0; 53 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 54 | 55 | int init_i2c_multiplexer() { 56 | i2cmult_fd=wiringPiI2CSetup(TCA954X_I2C_ADDRESS); 57 | if (i2cmult_fd>0) { 58 | wiringPiI2CReadReg8(i2cmult_fd, 0x0); 59 | return 1; 60 | } 61 | return 0; 62 | } 63 | 64 | void select_zyntof_chan(uint8_t i) { 65 | if (i2cmult_fd>0) { 66 | wiringPiI2CWriteReg8(i2cmult_fd, 0x0, 0xF&(0x1<MAX_TOF_DISTANCE) { 112 | v = 16384; 113 | return; 114 | } else { 115 | v = 16384 * (zyntofs[i].val - MIN_TOF_DISTANCE) / (MAX_TOF_DISTANCE - MIN_TOF_DISTANCE); 116 | } 117 | if (zyntofs[i].midi_evt==PITCH_BEND) { 118 | //Send MIDI event to engines and ouput (ZMOPS) 119 | zmip_send_pitchbend_change(ZMIP_FAKE_INT, zyntofs[i].midi_chan, v); 120 | //fprintf(stderr, "ZYNTOF [%d] => MIDI %d\n", i, v); 121 | } else { 122 | uint8_t mv = v>>7; 123 | if (mv!=zyntofs[i].midi_val) { 124 | //fprintf(stderr, "ZYNTOF [%d] => MIDI %d\n", i, mv); 125 | zyntofs[i].midi_val = mv; 126 | if (zyntofs[i].midi_evt==CTRL_CHANGE) { 127 | //Send MIDI event to engines and output (ZMOPS) 128 | zmip_send_ccontrol_change(ZMIP_FAKE_INT, zyntofs[i].midi_chan, zyntofs[i].midi_num, mv); 129 | //Send MIDI event to UI 130 | write_zynmidi_ccontrol_change(zyntofs[i].midi_chan, zyntofs[i].midi_num, mv); 131 | } else if (zyntofs[i].midi_evt==CHAN_PRESS) { 132 | //Send MIDI event to engines and ouput (ZMOPS) 133 | zmip_send_chan_press(ZMIP_FAKE_INT, zyntofs[i].midi_chan, mv); 134 | } 135 | } 136 | } 137 | } 138 | 139 | void * poll_zyntofs(void *arg) { 140 | int i; 141 | while (1) { 142 | for (i=0;i %d\n", i, zyntofs[i].val); 150 | } 151 | } 152 | usleep(POLL_ZYNTOFS_US); 153 | } 154 | return NULL; 155 | } 156 | 157 | pthread_t init_poll_zyntofs() { 158 | pthread_t tid; 159 | int err=pthread_create(&tid, NULL, &poll_zyntofs, NULL); 160 | if (err != 0) { 161 | fprintf(stderr, "ZynTOF: Can't create poll thread :[%s]", strerror(err)); 162 | return 0; 163 | } else { 164 | fprintf(stderr, "ZynTOF: Poll thread created successfully\n"); 165 | return tid; 166 | } 167 | } 168 | 169 | //----------------------------------------------------------------------------- 170 | // Zyntof Library Initialization 171 | //----------------------------------------------------------------------------- 172 | 173 | int init_zyntof() { 174 | int i; 175 | for (i=0;i 8 | * 9 | * ****************************************************************** 10 | * 11 | * This program is free software; you can redistribute it and/or 12 | * modify it under the terms of the GNU General Public License as 13 | * published by the Free Software Foundation; either version 2 of 14 | * the License, or any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * For a full copy of the GNU General Public License see the LICENSE.txt file. 22 | * 23 | * ****************************************************************** 24 | */ 25 | 26 | //----------------------------------------------------------------------------- 27 | // TCA954X (43/44/48) Stuff => I2C Multiplexer 28 | //----------------------------------------------------------------------------- 29 | 30 | //Default config for TCA9543 (I2C multiplexer) 31 | #define TCA954X_I2C_ADDRESS 0x70 32 | 33 | void select_zyntof_chan(uint8_t i); 34 | 35 | //----------------------------------------------------------------------------- 36 | // VL53L0X Stuff 37 | //----------------------------------------------------------------------------- 38 | 39 | #define VL53L0X_I2C_ADDRESS 0x29 40 | #define VL53L0X_DISTANCE_MODE 1 41 | 42 | //----------------------------------------------------------------------------- 43 | // Generate MIDI events from Distance 44 | //----------------------------------------------------------------------------- 45 | 46 | #define MAX_NUM_ZYNTOFS 4 47 | #define POLL_ZYNTOFS_US 1000 48 | 49 | #define MIN_TOF_DISTANCE 60 50 | #define MAX_TOF_DISTANCE 600 51 | 52 | struct zyntof_st { 53 | uint8_t enabled; 54 | uint8_t i; 55 | uint16_t val; 56 | 57 | uint8_t midi_evt; 58 | uint8_t midi_chan; 59 | uint8_t midi_num; 60 | uint8_t midi_val; 61 | }; 62 | 63 | void setup_zyntof(uint8_t i, uint8_t midi_evt, uint8_t midi_chan, uint8_t midi_num); 64 | void disable_zyntof(uint8_t i); 65 | void send_zyntof_midi(uint8_t i); 66 | 67 | pthread_t init_poll_zyntofs(); 68 | 69 | //----------------------------------------------------------------------------- 70 | // TOFs Library Initialization 71 | //----------------------------------------------------------------------------- 72 | 73 | int init_zyntof(); 74 | int end_zyntof(); 75 | 76 | //----------------------------------------------------------------------------- 77 | --------------------------------------------------------------------------------