├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── docs ├── hex │ └── hex-to-bin.py └── nts-1-customizations │ ├── libraries │ └── NTS-1 │ │ ├── README.md │ │ ├── examples │ │ ├── Blank_Template │ │ │ ├── Blank_Template.ino │ │ │ └── README.md │ │ └── Sequencer_Template │ │ │ ├── README.md │ │ │ └── Sequencer_Template.ino │ │ ├── keywords.txt │ │ ├── library.properties │ │ └── src │ │ ├── nts-1.cpp │ │ └── nts-1.h │ └── variants │ └── NTS1_REF_CP_REVC │ ├── nts1_iface.c │ └── nts1_iface.h ├── examples ├── 0-log-received.main.c ├── 1-note-on.main.c └── main_c ├── include └── README ├── lib ├── CMakeLists.txt └── README ├── platformio.ini ├── sdkconfig ├── src ├── CMakeLists.txt ├── main.c └── main.cpp └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | *.code-workspace 3 | platformio-device-monitor* 4 | src/tests/ 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/korg-nts-1-lib"] 2 | path = lib/korg-nts-1-lib 3 | url = git@github.com:eudes/korg-nts-1-lib.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < https://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < https://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < https://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choose one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to be used as a library with examples. 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(korg-nts1-lib) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Figuring out how to build a custom controller panel for the Korg NTS1 2 | ## Or: analyzing a SPI peripheral implementation 3 | ### Or: learning microcontroller programming by example 4 | 5 | ## DISCLAIMER 6 | I am not associated with Korg in any way. This analysis is still a work in progress. The code here might not work for you. I'm also not an expert in electronics or SoC programming. Anything I say here might be wrong, and if Korg ever publishes an official guide, you'll do better reading that, than this. This is a learning exercise. 7 | 8 | ## The reference board 9 | Korg provides an Open Hardware reference board, complete with a firmware that communicates with the NTS1. 10 | The board is based on a STM32, and that's the only microcontroller that is supported by the provided libraries. 11 | 12 | I don't happen to have a STM32 laying around, and my goal is more towards figuring out the communication protocol and learning about lower level (C/C++) microcontroller programming, than implementing any particular design for 13 | a controller panel. 14 | 15 | So what follows is an investigation on the inner workings of the implementation and, hopefully, another implementation for either the ESP32, the nFR52840, or the ATMega2560, which are the MCUs that I have on hand. 16 | 17 | ### Links 18 | The NTS1 reference panel lives at: 19 | https://github.com/korginc/nts-1-customizations 20 | 21 | The particular variant of the STM32 used for the board is the STM32F030R8T6. 22 | Here's some documentation for the MCU. 23 | 24 | Homepage: 25 | https://www.st.com/en/microcontrollers-microprocessors/stm32f030r8.html#resource 26 | 27 | Datasheet: 28 | https://www.st.com/resource/en/datasheet/stm32f030r8.pdf 29 | 30 | The reference manual: 31 | https://www.st.com/content/ccc/resource/technical/document/reference_manual/c2/f8/8a/f2/18/e6/43/96/DM00031936.pdf/files/DM00031936.pdf/jcr:content/translations/en.DM00031936.pdf 32 | 33 | The firmware does extensive use of the HAL peripheral. Here's the docs: 34 | https://www.st.com/resource/en/user_manual/dm00122015-description-of-stm32f0-hal-and-lowlayer-drivers-stmicroelectronics.pdf 35 | 36 | ## This repo 37 | - docs/nts-1-customizations : the original [`nts-1-customizations` repo](https://github.com/korginc/nts-1-customizations) code, with added comments and japanese sentences translated. 38 | - docs/hex : contains a python utility to translate hex messages into binary (0s and 1s). 39 | - examples/ : working examples for the topics I talk about in this page. Snippets are taken from there. 40 | - src/ : my latest progress, the code that gets uploaded to my ESP32. Code that works is taken from here and put into `examples/. 41 | 42 | ## The reference firmware 43 | The reference code is provided in two parts: 44 | - `nts1_iface.c` and `nts1_iface.h`: The STM32-specific implementation (Arduino/variants/NTS1_REF_CP_REVC). 45 | - `nts-1.cpp` and `nts-1.h`: A higher level class for use in Arduino files (Arduino/libraries/NTS-1). 46 | 47 | The basic idea of separating the MCU specific code from the higher level abstraction is solid. The problem is that **the whole communication protocol** is implemented in the STM32-specific code. I assume it's done like this because the SMT32 provides native facilities for SPI, including data buffers for managing the read and write processes to the actual GPIOs. However, this makes it a bit tricky to port the protocol, since you essentially have to rebuild the logic that doesn't deal directly with the microcontroller. The higher level class is just a wrapper around the STM32-specific code that doesn't do much. It would have been best to have the higher level class do more of the setup, and the specific implementation just deal with STM32 stuff. 48 | 49 | Since we need to reimplement the whole process, the first thing to do is understand what the library is doing under the hood. 50 | 51 | ### Arduino examples 52 | The code comes with a couple of examples, of which this is the simplest: 53 | ```c 54 | #include 55 | NTS1 nts1; 56 | 57 | void setup() { 58 | nts1.init(); 59 | } 60 | void loop() { 61 | nts1.idle(); 62 | } 63 | ``` 64 | 65 | The `nts1` methods are simple wrappers to the `nts1_iface.c` implementations, so we'll start from there. 66 | 67 | ### SPI 68 | The first thing you notice when reading the `nts-1.cpp` is that it mentions using `SPI` as a communication protocol. This is nice, since it's a basic full duplex protocol that doesn't have too much boilerplate to deal with. 69 | 70 | You have 4 signals: 71 | - CLK (clock): 72 | - shared. Clock signal. Writers should write on one of the slopes of the clock signal, and readers should sample on the opposite slope. Which slope to use is defined in the SPI Mode. The master ouputs the clock and slaves follow it. 73 | - CS (chip select)/ SS(N) (slave select): 74 | - one line per slave. The line will normally be HIGH, and the master will make it LOW when it wants to send or receive data for that particular slave. 75 | - MOSI (master out, slave in): 76 | - shared. The line where the master will send data to the slaves. 77 | - MISO (master in, slave out) 78 | - shared. The line where the slaves will send data to the master. 79 | 80 | SPI Modes are important to take into account, since it dictates which slopes of the clock to use for sampling 81 | (clock phase, CPHA), and the clock polarity (CPOL). Clock polarity indicates what the base state of the clock is when switching SS low to enable the slave, and the slave starts sampling. 82 | This translates as follows: 83 | - Mode 0: CPOL: 0, CPHA: 0. Clock starts low, data sampled on the first edge, which is the rising edge. 84 | - Mode 1: CPOL: 0, CPHA: 1. Clock start low, data sampled on the second edge, which is the falling edge. 85 | - Mode 2: CPOL: 1, CPHA: 0. Clock starts high, data sampled on the first edge, which is the falling edge. 86 | - Mode 3: CPOL: 1, CPHA: 1. Clock start high, data sampled on the second edge, which is the rising edge. 87 | 88 | We also need to establish what the bit order is in each chunk of information (LSB, or MSB). 89 | 90 | ### Finding the SPI settings 91 | The SPI mode must be known in advance, since SPI doesn't advertise it. The idea is that a master will be able to accomodate different slaves by switching its own settings on the fly when addressing them. 92 | 93 | `nts1_iface.c` configures the STM32 as follows: 94 | ```c 95 | static inline void s_spi_struct_init(SPI_InitTypeDef* SPI_InitStruct) 96 | { 97 | SPI_InitStruct->Mode = SPI_MODE_SLAVE; 98 | SPI_InitStruct->Direction = SPI_DIRECTION_2LINES; 99 | SPI_InitStruct->DataSize = SPI_DATASIZE_8BIT; 100 | SPI_InitStruct->CLKPolarity = SPI_POLARITY_HIGH; 101 | SPI_InitStruct->CLKPhase = SPI_PHASE_2EDGE; 102 | SPI_InitStruct->NSS = SPI_NSS_SOFT; 103 | SPI_InitStruct->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; 104 | SPI_InitStruct->FirstBit = SPI_FIRSTBIT_LSB; 105 | SPI_InitStruct->TIMode = SPI_TIMODE_DISABLE; 106 | SPI_InitStruct->CRCCalculation = SPI_CRCCALCULATION_DISABLE; 107 | SPI_InitStruct->CRCPolynomial = 7; 108 | SPI_InitStruct->CRCLength = SPI_CRC_LENGTH_DATASIZE; 109 | SPI_InitStruct->NSSPMode = SPI_NSS_PULSE_DISABLE; 110 | } 111 | ``` 112 | 113 | This tells us that: 114 | - The panel is working as a **slave**. 115 | - Since it's operating in slave mode, it doesn't need to set a clock speed (it will follow the signal from master). 116 | - The communication is full duplex (`SPI_InitStruct->Direction = SPI_DIRECTION_2LINES`). SPI is full duplex by default, but some chips let you use just half of that if you don't need it. 117 | - It's working with 8 bit packets. 118 | - CPOL is HIGH (1), and CPHA is (1), so our SPI Mode is 3. 119 | - SS is "soft". This means that it's controlled via software, instead of hardware. There's a mention of "No NSS" in the code, so we can assume that SS is always ON. 120 | - Bit order is LSB 121 | - TIMode refers to some quirks in the Texas Instrument implementation of SPI, but since it's disabled, I won't worry too much about it yet. 122 | - CRC is disabled, so no automatic verification is done on the data sent. 123 | - NSSPMode: if enabled, it produces a pulse between each data frame to signal that the device is done reading. It's disabled so I won't worry about it. 124 | - BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2. I'm not quite sure why this is set here. The prescaler is used to divide the system clock by a factor when working as master. As a slave, it shouldn't matter. Might be a default from the IDE. 125 | 126 | ### STM32 SPI 127 | The STM32 provides hardware support for SPI communication. This means that we don't directly control the GPIO pins to send or receive data. Instead, the STM32 provides C vectors that write to FIFO registers directly, and data is transmitted to the pins from there. 128 | 129 | This saves us the hassle of controlling the timing of the messages, since sampling and shifting is taken care of and trasmitted to the FIFO queue for us. 130 | 131 | ### NTS1 init 132 | We'll analyse the setup process, which should give us a few clues about how the panel works. 133 | 134 | This is the method called on setup: 135 | ```c 136 | nts1_status_t nts1_init() 137 | { 138 | // Enables sampling the GPIO pin at clock rate 139 | // This is mapped to __HAL_RCC_GPIOB_CLK_ENABLE 140 | ACK_GPIO_CLK_ENABLE(); 141 | 142 | GPIO_InitTypeDef gpio; 143 | 144 | // Configures a GPIO pin for ACK (acknowledge message) 145 | /* PANEL ACK */ 146 | gpio.Mode = GPIO_MODE_OUTPUT_PP; 147 | gpio.Speed = GPIO_SPEED_FREQ_HIGH; 148 | gpio.Pull = GPIO_NOPULL; 149 | gpio.Alternate = 0; 150 | gpio.Pin = ACK_PIN; 151 | HAL_GPIO_Init(ACK_PORT, &gpio); 152 | 153 | // More on this below 154 | HAL_StatusTypeDef res = s_spi_init(); 155 | if (res != HAL_OK) 156 | return (nts1_status_t)res; 157 | 158 | // Fills the hardware transmission FIFO register with dummy data 159 | // Fill TX FIFO 160 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 161 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 162 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 163 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 164 | //*/ 165 | 166 | // Sets the ACK pin to 1 167 | // More below 168 | s_port_startup_ack(); 169 | s_started = true; 170 | 171 | return k_nts1_status_ok; 172 | } 173 | 174 | HAL_StatusTypeDef s_spi_init() 175 | { 176 | // Enables the clock for the SYSCFG register 177 | // which is used for specific configurations of memory and 178 | // DMA requests remap and to control special I/O features. 179 | __HAL_RCC_SYSCFG_CLK_ENABLE(); 180 | 181 | // Configures the GPIO pins for SPI 182 | // See below 183 | s_spi_enable_pins(); 184 | 185 | // Resets and enables clock signals for SPI 186 | SPI_FORCE_RESET(); 187 | SPI_RELEASE_RESET(); 188 | SPI_CLK_ENABLE(); 189 | 190 | // Configures SPI as seen above 191 | // Note that SPI_PERIPH is mapped to SPI2, which is one 192 | // of the 2 available SPI devices in the MCU 193 | s_spi.Instance = SPI_PERIPH; 194 | s_spi_struct_init(&(s_spi.Init)); 195 | 196 | // Initializes the SPI driver 197 | const HAL_StatusTypeDef res = HAL_SPI_Init(&s_spi); 198 | if (res != HAL_OK) { 199 | return res; 200 | } 201 | 202 | // Sets the interrupt handler in the NVIC (Nested Vector Interrupt Controller) 203 | HAL_NVIC_SetPriority(SPI_IRQn, SPI_IRQ_PRIORITY, 0); 204 | HAL_NVIC_EnableIRQ(SPI_IRQn); 205 | 206 | // Adds (bitwise OR) RXNE to the SPI control register 2 207 | // This enables the "RX buffer not empty" interrupt 208 | // See Reference Manual 28.5.10 209 | SPI_PERIPH->CR2 |= SPI_IT_RXNE; 210 | 211 | // Empties the buffers for transmission and reception 212 | // and reset counters 213 | s_panel_rx_status = 0; 214 | s_panel_rx_data_cnt = 0; 215 | SPI_RX_BUF_RESET(); 216 | SPI_TX_BUF_RESET(); 217 | 218 | // Finally enables HAL SPI 219 | __HAL_SPI_ENABLE(&s_spi); 220 | 221 | return HAL_OK; 222 | } 223 | 224 | 225 | static inline void s_spi_enable_pins() 226 | { 227 | // Same as ACK_GPIO_CLK_ENABLE, 228 | // mapped to __HAL_RCC_GPIOB_CLK_ENABLE 229 | SPI_GPIO_CLK_ENA(); 230 | 231 | GPIO_InitTypeDef gpio; 232 | 233 | /* Enable SCK, MOSI, MISO. No NSS. */ 234 | /* Peripherals alternate function */ 235 | gpio.Mode = GPIO_MODE_AF_PP; 236 | gpio.Speed = GPIO_SPEED_FREQ_HIGH; 237 | gpio.Pull = GPIO_NOPULL; 238 | // GPIO pins set to alternate function, which allows alternating the pins 239 | // between GPIO functions and internal peripheral functions 240 | // This is a requirement of the SPI device in the STM32 241 | // See Reference Manual, Figure 274 242 | gpio.Alternate = SPI_GPIO_AF; 243 | 244 | gpio.Pin = SPI_MISO_PIN; 245 | HAL_GPIO_Init(SPI_MISO_PORT, &gpio); 246 | 247 | gpio.Pin = SPI_MOSI_PIN; 248 | HAL_GPIO_Init(SPI_MOSI_PORT, &gpio); 249 | 250 | gpio.Pin = SPI_SCK_PIN; 251 | gpio.Pull = GPIO_PULLUP; 252 | HAL_GPIO_Init(SPI_SCK_PORT, &gpio); 253 | 254 | } 255 | 256 | static inline void s_port_startup_ack(void) 257 | { 258 | // This sets the ACK_PIN GPIO pin to 1 259 | // ACK_PORT is the GPIO device (of which the STM32 has 2) 260 | // BSRR is the Bit Set Register 261 | // Setting this register will write to the GPIO pin 262 | // This is an atomic operation (takes 1 system clock tick), 263 | // as oppossed to calling a method which would take more time 264 | ACK_PORT->BSRR = ACK_PIN; 265 | } 266 | 267 | static inline void s_port_wait_ack(void) 268 | { 269 | // This sets the ACK_PIN GPIO pin to 0 270 | // Same as above, but the register is the Bit Reset Register 271 | ACK_PORT->BRR = ACK_PIN; 272 | } 273 | ``` 274 | 275 | Summing up, the setup method: 276 | - Initializes the GPIO pins as needed for SPI, and for ACK. 277 | - Enables the clock for the different internal devices. 278 | - Resets the buffers and internal variables. 279 | 280 | Here, it's also intersting to note that ACK is not part of SPI, so this must be a custom mechanism for the NTS1 to detect the panel and start SPI communications. 281 | 282 | ### The loop 283 | Once the `setup()` method is finished, our `loop()` method is called repeatedly. This calls `NTS1.idle()` as a first and only step. It won't do much, but there will probably something to learn in it. 284 | 285 | ```c 286 | nts1_status_t nts1_idle() 287 | { 288 | // Return of HOST communication Check 289 | // This should be true right after executing setup() 290 | if (s_started) { 291 | // Checks if the reception buffer is not full 292 | if (s_spi_chk_rx_buf_space(32)) { 293 | // Sets the ACK pin to 1 294 | s_port_startup_ack(); 295 | } 296 | } 297 | 298 | // HOST I/F Give priority to Idle processing of received data 299 | // As long as the reception buffer is not empty 300 | while (!SPI_RX_BUF_EMPTY()) { 301 | // Reads from the buffer and executes the handler 302 | // Data in receive buffer 303 | s_rx_msg_handler(s_spi_rx_buf_read()); 304 | } 305 | } 306 | ``` 307 | 308 | We won't get into much detail about what the handler does, but it deals with messages sent from the NTS1 to the peripheral, like NOTE ON messages, param changes, etc. 309 | 310 | There's still meat in this bone, with respect to how data is read from the RX buffer, but we should talk first about how data gets into that buffer first. 311 | 312 | ### Interrupts 313 | As we saw in the setup method, interrupts are enabled for RXNE. They happen whenever the Reception FIFO register is not empty, meaning that an interrupt will be trigger whenever there's data in the input FIFO register. 314 | 315 | This is used to load the software reception buffer with data from the FIFO register, and clear the data from the register to allow more data to enter. 316 | 317 | The code that handles the interrupt is as follows: 318 | ```c 319 | extern void SPI_IRQ_HANDLER() 320 | { 321 | volatile uint16_t sr; 322 | uint8_t txdata, rxdata; 323 | 324 | // HOST -> PANEL 325 | // While the RX FIFO is not empty 326 | // This is signified by the RXNE flag in the SPI Status Registry being 1 327 | // and calculated from the SPI Status registry 328 | // by doing a bitwise AND between the SPI_SR_RXNE flag and the SR itself 329 | // see Reference Manual 28.9.3 330 | while ((sr = SPI_PERIPH->SR) & SPI_SR_RXNE) { 331 | // Take out 8 bits from the FIFO 332 | rxdata = s_spi_raw_fifo_pop8(SPI_PERIPH); // DR read clears RXNE flag 333 | // And write it into the software RX buffer 334 | // This function only writes if the buffer has enough space to accomodate the byte 335 | if (!s_spi_rx_buf_write(rxdata)) { 336 | // If there's not enough space in the buffer 337 | // this resets the index read and write indexes for the buffer 338 | // which will cause it to start writing into the buffer from the beginning 339 | 340 | // If RxBuf is full, reset it. 341 | SPI_RX_BUF_RESET(); 342 | } 343 | else { 344 | // If there is enough space 345 | if (!s_spi_chk_rx_buf_space(32)) { 346 | // and the buffer cannot accomodate 32 bits more 347 | // the ACK pin is set to 0 348 | s_port_wait_ack(); 349 | // I assume this means the NTS1 will stop sending data 350 | } else { //Remaining buffer 351 | // otherwise the ACK pin is set to 1 352 | s_port_startup_ack(); 353 | // which will allow the NTS1 to send data 354 | } 355 | } 356 | } 357 | // now the rx FIFO is empty (the RXNE flag has cleared) 358 | 359 | // HOST <- PANEL 360 | if (!SPI_TX_BUF_EMPTY()) { // Send buffer has data 361 | // If there's data to be sent 362 | txdata = s_spi_tx_buf_read(); 363 | if (txdata & 0x80) { // In Status, check whether EndMark is added. 364 | // and the data contains 0x80 365 | if (!SPI_TX_BUF_EMPTY()) { // There is data to be sent next in the send buffer 366 | // an EMARK is set on the data 367 | txdata |= PANEL_CMD_EMARK; 368 | // Note: this will set endmark on almost any status, especially those who have pending data, 369 | // which seems to contradict the endmark common usage of marking only the last command of a group 370 | } 371 | } 372 | // Data is sent to the Tx FIFO register 373 | s_spi_raw_fifo_push8(SPI_PERIPH, txdata); 374 | } 375 | else { // Set the dummy because the send buffer is empty. 376 | // Dummy data is sent to the TX FIFO register 377 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 378 | } 379 | } 380 | ``` 381 | 382 | It's interesting to note that the interrupt handler does both the reading and the writing to the Rx and Tx registers. s_spi_raw_fifo_push8() is only called from here (and the setup method). This means that the panel only sends data when there's data in the input FIFO. The public "send/write" methods for the NTS1 class only write to the software Tx buffer. 383 | 384 | ### Writing to the SPI FIFO 385 | The following are the only two places where direct access to the FIFO registers happens. This code is interesting because of the way the memory address for the SPI_DR register is calculated and derefenced. 386 | 387 | ```c 388 | static inline void s_spi_raw_fifo_push8(SPI_TypeDef* SPIx, uint8_t data) 389 | { 390 | // SPIx is a pointer, so will a contain a memory address of the SPI device 391 | // 0x0C is the offset of the Data Register with respect to the SPI device. 392 | // The casting "(uint32_t)" is necessary here because SPIx is a pointer address, 393 | // but it's trying to save it into a non-pointer variable. The compiler won't allow 394 | // this unless you force it by doing the casting. 395 | const uint32_t spix_dr = (uint32_t)SPIx + 0x0C; 396 | // spix_dr is the address of the SPIX_DR data register 397 | // Reading from this address will return the oldest frame of data from the Rx FIFO 398 | // Writing to this address will write to the end of the Tx FIFO 399 | 400 | // In this case, we are writing to it 401 | *(__IO uint8_t *) spix_dr = data; 402 | // *(__IO uint8_t *) spix_dr = data means: 403 | // - (__IO uint8_t *) spix_dr: cast spidx_dr to a pointer (*) of type __IO uint8_t 404 | // - first "*": derreference the pointer we just casted and 405 | // - "= data": assign the value of data to the derefferenced location 406 | // In other words, it's equivalent to: 407 | // __IO uint8_t *ptr_spix_dr = (__IO uint8_t *) spix_dr; // again, casting to change from uint to *uint 408 | // *ptr_spix_dr = data; 409 | } 410 | 411 | static inline uint8_t s_spi_raw_fifo_pop8(SPI_TypeDef* SPIx) 412 | { 413 | const uint32_t spix_dr = (uint32_t)SPIx + 0x0C; 414 | 415 | // In this case, we are reading it 416 | return *(__IO uint8_t *) spix_dr; 417 | } 418 | ``` 419 | The FIFO registers are 32 bits. A buffer overrun event is triggered if data is written to them when full. 420 | An interrupt is triggered when the FIFOs are ready to be used: 421 | - Rx FIFO: the RXNE (not empty) interrupt is triggered whenever a frame has entered the FIFO, meaning that data should be read out of it. 422 | - Tx FIFO: the TXE (empty) interrupt is triggered when the TXFIFO level is less than or equal to half of its capacity, meaning that data can be sent to it. 423 | 424 | Both RXNE and TXE are flags that can be polled, or be set to trigger an interrupt, as the code here is doing for RXNE. 425 | 426 | ## A testing application 427 | With the setup covered, we can begin building an application that will allow us to connect to the NTS1 controller, set our ACK pin to 1, and start receiving data. 428 | 429 | ### ESP32 programming with platformio and espidf 430 | I'll base my test application on the ESP32 MCU, using platformio with the ESP IDF. This means that I won't be producing any Arduino code for now, and I'll program directly against the ESP32 specific libraries. Doing this will simplify the design of the test app, given that most MCUs come with development kits that contain working examples for using their different peripherals, SPI included. 431 | 432 | I'll use platformio instead of the barebones ESP IDF because a package manager + build tool that does most of the heavy lifting out of the box is a great addition to any toolbelt. It'll simplify installing and using the tools needed to flash the chip and install vendor libraries. 433 | 434 | You can find all the information you need on how to start a new platformio project for the ESP32 here: https://docs.platformio.org/en/latest/platforms/espressif32.html 435 | 436 | ### Identifying the NTS1 pins 437 | Before we start building the test app, we need to identify the pins the NTS1 uses for SPI communication and ACK. 438 | 439 | Looking at the included schematic for the reference panel, we can see that the headers used to connect to the NTS1 one are labelled "MAIN CONNECTOR". There are 2 rows of pins, labelled CN2 and CN7, and their pins are named as follows: 440 | Header | Pin N | Name | STM32 Pin 441 | -------| ----- | ---------- | --------- 442 | CN2 | 1 | GND | - 443 | CN2 | 2 | 3V3 | - 444 | CN2 | 5 | PANEL_ACK | PB12 445 | CN2 | 7 | GND | - 446 | CN7 | 1 | GND | - 447 | CN7 | 2 | CK_PNL | PB13 (SPI2_SCK) 448 | CN7 | 3 | RX_PNL | PB15 (SPI2_MOSI) 449 | CN7 | 4 | TX_PNL | PB14 (SPI2_MISO) 450 | CN7 | 5 | RESET_PNL | - 451 | CN7 | 6 | BOOT_PNL | - 452 | CN7 | 7 | GND | - 453 | 454 | From looking at the Gerber files (and a video that I can't seem to find now), it seems CN2 corresponds to the left side header in the NTS1. Pins start from 1 to 7, bottom side first (closer to the headphone jack). Testing with an LED between the left hand side bottommost pin (CN21) and other pins confirms this hypothesis. 455 | 456 | So, we'll use the following pins (in the NTS1): 457 | - LHS2: 3V3 458 | - LHS5: ACK 459 | - RHS1: GND 460 | - RHS2: SPI_CLK 461 | - RHS3: SPI_RX (MOSI) 462 | - RHS4: SPI_TX (MISO) 463 | 464 | ESP32 (Sparkfun Thing plus): 465 | NTS1 | GPIO | Name 466 | -------| ------- | -------- 467 | LHS2 | 3V3 | 3V3 468 | LHS5 | 22 | PANEL_ACK 469 | RHS1 | GND | GND 470 | RHS2 | 18 | CK_PNL 471 | RHS3 | 23 | MOSI 472 | RHS4 | 19 | MISO 473 | 474 | Using SPI3 (VSPI) as slave. 475 | 476 | Header | Pin N | Name | STM32 Pin 477 | -------| ----- | ---------- | --------- 478 | CN2 | 1 | GND | - 479 | CN2 | 2 | 3V3 | - 480 | CN2 | 5 | PANEL_ACK | PB12 481 | CN2 | 7 | GND | - 482 | CN7 | 1 | GND | - 483 | CN7 | 2 | CK_PNL | PB13 (SPI2_SCK) 484 | CN7 | 3 | RX_PNL | PB15 (SPI2_MOSI) 485 | CN7 | 4 | TX_PNL | PB14 (SPI2_MISO) 486 | CN7 | 5 | RESET_PNL | - 487 | CN7 | 6 | BOOT_PNL | - 488 | CN7 | 7 | GND | - 489 | 490 | ### Finding the clock frequency 491 | The NTS1 starts the clock when the ACK pin is on. We can use this to obtain the clock frequency by using a logic analyzer and PulseView or an oscilloscope. I went with a very cheap 24MHz logic analyzer (10€ with fast shipping, cheaper if you're willing to wait and order in AliExpress or Ebay). 492 | 493 | I set the NTS1 ACK pin to +3v3, and connected the logic analyzer to the CLK pin. The period of the clock is 1us, which means that the frequency is 1MHz. This is good news because: 494 | - The ESP32 can accept clock frequencies from 2.5KHz to 10MHz. 495 | - The speed is not very high, so most oscilloscopes and logic analyzers should be able to deal with it easily. 496 | 497 | ### Modifying the "SPI slave" example project 498 | The ESP32 IDF comes with an example for SPI slave mode, which we'll use as a base for the test application. 499 | https://github.com/espressif/esp-idf/tree/master/examples/peripherals/spi_slave/receiver/main 500 | 501 | This the modified example. It uses the ESP32 IDF spi-slave driver to configure the device and establish a SPI connection. Things to take into account: 502 | - It uses the IO_MUX pins for SPI, so that communication doesn't get delayed by the GPIO matrix. This seems important, I tried without it and the messages didn't produce anything clear. 503 | - We are changing the default bit order to LSB, like the reference panel does. 504 | - The example also includes a handshake/ACK routine, so that's nice, although it might work in a different way. Using the default for now seems to produce some results. 505 | - The ESP32 SPI driver doesn't have a "software" CS pin, nor does it work without it. I couldn't identify a CS pin in the NTS1, so instead I leave it always on by enabling the pulldown resistor for that pin. 506 | 507 | `examples/0-log-received.main.c` 508 | ```c 509 | ... 510 | while(1){ 511 | //Clear receive buffer 512 | memset(s_spi_rx_buf, 0x0, SPI_RX_BUF_SIZE); 513 | memset(s_spi_tx_buf, 0x0, SPI_TX_BUF_SIZE); 514 | 515 | //Set up a transaction of 128 bytes to send/receive 516 | t.length=SPI_RX_BUF_SIZE*4; 517 | t.tx_buffer=s_spi_tx_buf; 518 | t.rx_buffer=s_spi_rx_buf; 519 | 520 | /* This call enables the SPI slave interface to send/receive to the sendbuf and recvbuf. The transaction is 521 | initialized by the SPI master, however, so it will not actually happen until the master starts a hardware transaction 522 | by pulling CS low and pulsing the clock etc. In this specific example, we use the handshake line, pulled up by the 523 | .post_setup_cb callback that is called as soon as a transaction is ready, to let the master know it is free to transfer 524 | data. 525 | */ 526 | ret=spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY); 527 | 528 | //spi_slave_transmit does not return until the master has done a transmission, so by here we have sent our data and 529 | //received data from the master. Print it. 530 | uint8_t* cp = s_spi_rx_buf; 531 | for (uint8_t i = 0; i < SPI_RX_BUF_SIZE; ++cp) 532 | { 533 | printf("%02x", *cp); 534 | i += 1; 535 | } 536 | printf("\n\n"); 537 | } 538 | ... 539 | ``` 540 | 541 | ### The Rx handler 542 | In order to understand the messages that the NTS1 will be sending, we need to start looking at the actual message handler: 543 | ```c 544 | 545 | // These are constants that will be used in the method 546 | 547 | #define PANEL_ID_MASK 0x38 // Bits 3-5 // 00111000 548 | #define PANEL_CMD_EMARK 0x40 // Bit 6 // 01000000 549 | #define PANEL_START_BIT 0x80 // Bit 7 // 10000000 550 | 551 | static uint8_t s_panel_id = PANEL_ID_MASK; // Bits 3-5 "ppp"="111" 552 | static uint8_t s_dummy_tx_cmd = (PANEL_ID_MASK + 0xC7); // B'11ppp111; 553 | 554 | enum { 555 | k_rx_cmd_event = 0x84U, // 10000100 556 | k_rx_cmd_param = 0x85U, // 10000101 557 | k_rx_cmd_other = 0x86U, // 10000110 558 | k_rx_cmd_dummy = 0x87U // 10000111 559 | }; 560 | 561 | // This is the handler itself 562 | static void s_rx_msg_handler(uint8_t data) 563 | { 564 | // data is 1 byte of the input buffer 565 | // If data byte starts with 10000000 566 | if (data >= 0x80) { 567 | // Status byte 568 | // resets the counter 569 | s_panel_rx_data_cnt = 0; 570 | // data = data AND not(PANEL_CMD_EMARK) 571 | // PANEL_CMD_EMARK is 01000000 572 | // not(PANEL_CMD_EMARK) is 10111111 573 | // so, the following discards the bit at position 6 (starting from pos 0) 574 | data &= ~PANEL_CMD_EMARK; 575 | 576 | if (data == 0xBEU) { // 10111110:Panel ID allocation 577 | // if data == 1x111110 578 | s_panel_rx_status = data & ~PANEL_ID_MASK; // discards bits 3,4 and 5 579 | //s_panel_rx_status = 10111110 * 11000111 => 10000110 580 | } else if ( 581 | (data & PANEL_ID_MASK) // bits 3 4 5 of data (00xxx000) 582 | == 583 | (s_panel_id & PANEL_ID_MASK) // 00111000 584 | ) { // if data contains (xx111xxx) 585 | s_panel_rx_status = data & ~PANEL_ID_MASK; // produces 1x000xxx 586 | } else { 587 | s_panel_rx_status = 0; // cancel any previous command reception 588 | } 589 | // exits the method to parse next byte 590 | return; 591 | } 592 | // the previous section stores in s_panel_rx_status the first byte 593 | // received, identified by having its first bit set to 1 (1xxxxxxx) 594 | // and it processes it to discard irrelevant data 595 | 596 | // Relevant statuses: 597 | // if data == 1x111110 => active_cmd = 10000110 598 | // if data == 1x111xxx => active_cmd = 10000xxx 599 | // otherwise, command is discarded 600 | 601 | // Stored status byte 602 | const uint8_t active_cmd = s_panel_rx_status; 603 | 604 | // Now we process bytes that don't start with 1 605 | switch (active_cmd) { 606 | case k_rx_cmd_event: // 1x111 100 607 | // ...does stuff 608 | break; 609 | case k_rx_cmd_param: // 1x111 101 610 | // ...does stuff 611 | break; 612 | case k_rx_cmd_other: // 1x111 110 613 | // ...does stuff 614 | break; 615 | case k_rx_cmd_dummy: // 10000111 616 | // continues to default 617 | default: 618 | // resets 619 | s_panel_rx_status = 0; // Clear save status 620 | s_panel_rx_data_cnt = 0; // Initialize data count 621 | break; 622 | } 623 | } 624 | ``` 625 | 626 | It looks like it reads the line byte by byte, waiting for a valid status byte to be received, which must have a predefined format, and then continues to process more bytes for each of those command. Once the handler has a valid status byte saved, it continues to process the following bytes, differently for every command. 627 | 628 | ### Command messages 629 | The one thing command messages do have in common is the format of the first bytes: 630 | ``` 631 | /*++++++++++++++++++++++++++++++++++++++++++++++ 632 | CMD4 : Event 633 | 1st :[1][0][ppp][100] 634 | 2nd :[0][sssssss] Size 635 | 3rd :[0][eeeeeee] Event ID 636 | 4th :[0][ddddddd] Data word 637 | ... 638 | +++++++++++++++++++++++++++++++++++++++++++++*/ 639 | ``` 640 | - 1st is always the status byte/command selector 641 | - 2nd is always the size of the message 642 | - 3rd is the sub-command or event ID 643 | - 4th is data associated to that command 644 | 645 | ### Finding status bytes 646 | As we saw before, the handler follows these rules to tell apart status bytes from other bytes: 647 | - Statuses start with 1 648 | - The very next bit is always discarded. 649 | - It accepts any message that conforms to this format: 1x111xxx. 650 | - It accepts one particular message (1x111110), that is for some reason treated differently. 651 | 652 | We can translate the hex strings into binary bytes and look for 1x111xxx.To make it easier, we can use a regex for that: /1[0-1]111[0-1]{3}/. 653 | 654 | However, in my many attempts to receive something intelligible from the NTS1, I failed misserably. I supect my SPI timing is a bit off. I did get to send note-on messages and make it sound, though. 655 | 656 | **Update**: I managed to get messages from the NTS-1 eventually. The problem at this point was that you need to pull one of the NTS-1 pins up in order to make it talk back to the panel, which I wasn't doing. This was present in the reference schematic, but up until this point I was going with just a "minimal" connection, meaning only the SPI and ACK pins connected. 657 | 658 | #### A tribute to lost time 659 | I'm making an aside here to say that, when I got to this point of the analysis (reading the data from the NTS1), I lost a couple of evenings because, of pin misnomer (MISO for MOSI etc.), and because, apparently, you cannot simply connect your ground to any of the available grounds. I only managed to make it work when I connected the RHS pin 1 ground to my ground. The rest of them (which I assumed should have worked the same), did not work properly. I guess, even though they are sent to ground **in the reference panel**, they are not connected to ground in the NTS1. 660 | 661 | So, here, toast with me to lost time and lessons (eventually) learned. 662 | 663 | This is also why I went into detail about the Commands before. Given that we are not actually getting any interesting commands, the next thing to do is to test that we can send commands to the NTS1 and see if it does anything. 664 | 665 | ### Sending commands 666 | Once we have the base SPI connection working, we can start sending messages to test that the NTS1 receives them. 667 | 668 | We saw in the Interrupt handler that the reference panel writes commands from its software buffer to the TX FIFO. These messages are put into the software user by the user, by calling one of the public methods in the `nts1_iface.h` class, or one of its equivalents in the arduino library (`nts-1.h`). We can follow the trace of one of these methods grab an example message. The most obvious one to try first is the `note_on` message, since that will make the NTS1 bleep and bloop for us. 669 | 670 | ```c 671 | 672 | nts1_status_t nts1_note_on(uint8_t note, uint8_t velo) { 673 | nts1_tx_event_t event; 674 | event.event_id = k_nts1_tx_event_id_note_on; 675 | event.msb = note & 0x7F; 676 | event.lsb = velo & 0x7F; 677 | return nts1_send_event(&event); 678 | } 679 | static inline nts1_status_t nts1_send_event(nts1_tx_event_t *event) { 680 | return nts1_send_events(event, 1); 681 | } 682 | 683 | nts1_status_t nts1_send_events(nts1_tx_event_t *events, uint8_t count) 684 | { 685 | assert(events != NULL); 686 | for (uint8_t i=0; i < count; ++i) { 687 | if (!s_tx_cmd_event(&events[i], (i == count-1))) { 688 | return k_nts1_status_busy; 689 | } 690 | } 691 | return k_nts1_status_ok; 692 | } 693 | 694 | static uint8_t s_tx_cmd_event(const nts1_tx_event_t *event, uint8_t endmark) 695 | { 696 | assert(event != NULL); 697 | if (!s_spi_chk_tx_buf_space(4)) 698 | return false; 699 | // if (s_panel_id & PANEL_ID_MASK) + (endmark) > 0, add PANEL_CMD_EMARK bit to the 700 | // command byte 701 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_event | PANEL_CMD_EMARK) : k_tx_cmd_event; 702 | s_spi_tx_buf_write(cmd); 703 | // mask out the Most Significant bit, just in case I guess, so it 704 | // doesn't get confused with a command byte 705 | s_spi_tx_buf_write(event->event_id & 0x7F); 706 | s_spi_tx_buf_write(event->msb & 0x7F); 707 | s_spi_tx_buf_write(event->lsb & 0x7F); 708 | return true; 709 | } 710 | // writes the byte to the buffer at the appropiate position 711 | // and moves the buffer pointers 712 | static void s_spi_tx_buf_write(uint8_t data) 713 | { 714 | s_spi_tx_buf[SPI_TX_BUF_MASK & s_spi_tx_widx] = data; 715 | s_spi_tx_widx = SPI_BUF_INC(s_spi_tx_widx, SPI_TX_BUF_SIZE); 716 | } 717 | 718 | ``` 719 | 720 | The logic here is straight forward: a call to the nts1_note_on(note, velocity) method will start chain of methods that will format the message appropiately and manage the internal status of the firmware. 721 | 722 | The bytes that end up in the buffer are: 723 | - s_spi_tx_buf[0] = 196; // note on command, with ENDMARK 724 | - s_spi_tx_buf[1] = 1; // size, pressumably // TODO this seems wrong 725 | - s_spi_tx_buf[2] = note; // note number (0-127) 726 | - s_spi_tx_buf[3] = 107; // velocity 727 | 728 | ### Putting it together 729 | With this we can test our assumptions and build a firmware that will send note-on mesages from our controller. 730 | 731 | `examples/1-note-on.main.c` 732 | ```c 733 | ... 734 | // every 21 iterations, send a note on message 735 | if (n%21 == 0) { 736 | s_spi_tx_buf[0] = 196; 737 | 738 | s_spi_tx_buf[1] = 1; 739 | s_spi_tx_buf[2] = note; 740 | s_spi_tx_buf[3] = 10; 741 | 742 | // if we get to the max number, change the direction 743 | if(note == 127){ 744 | increase = 0; 745 | } 746 | if(note == 20) { 747 | increase = 1; 748 | } 749 | // increase or decrease the note number 750 | note = increase ? note + 1 : note - 1; 751 | } 752 | 753 | //Set up a transaction of 128 bytes to send/receive 754 | t.length=SPI_RX_BUF_SIZE*4; 755 | t.tx_buffer=s_spi_tx_buf; 756 | t.rx_buffer=s_spi_rx_buf; 757 | 758 | ret=spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY); 759 | ... 760 | ``` 761 | ------ 762 | 763 | ## Refactoring for multiple architecture support 764 | ### Compile-time selection 765 | In order to make the original Arduino libray work for both the ESP32 and the STM32, we need first to refactor the original to select the appropiate implementation at compile time. We can achieve this by using compile-time flags. 766 | 767 | Both the STM32 and the ESP32 provide flags when compiling the project that you can use to detect which kind of platform your compiling for. We can leverage this by surrounding our framework-specific code in preprocessor if statements, so that only the code for the implementation is built. 768 | 769 | I created a new file `nts1_stm32.c`, where I'll put the STM32-specific code. I'll do the same for the ESP32 code. 770 | ```c 771 | #if defined(STM32F0xx) 772 | // the code 773 | #endif 774 | ``` 775 | 776 | ### Separating the specifics 777 | From my analysis of the code, I identified the STM32-specific code and moved it to `nts1_stm32.c`. 778 | I also created a new header file `nts1_impl.h` that will declare the specific elements that need to be accesible from the generic code (which will stay in `nts1_iface.h` and `nts1_iface.c`). This new header also marks the way for new implementations, since it declares which functions need to be implemented to support any other architecture/framework. 779 | 780 | ### The ESP32 implementation 781 | We now have a clear-cut interface that we need to implement (`nts1_impl.h`) for the ESP32, so let's take a look at how to replicate what the STM32 code is doing, for the ESP32. 782 | 783 | ```c 784 | nts1_status_t nts1_init(); 785 | nts1_status_t nts1_idle(); 786 | ``` 787 | 788 | #### nts1_init() 789 | The first thing to do is initialize the device to prepare it to talk with the NTS-1. This is done in the `nts1_init()` method. For the ESP32, we can do it as follows: 790 | ```c 791 | 792 | nts1_status_t nts1_init() 793 | { 794 | // Init the ACK GPIO pin 795 | s_ack_init(); 796 | 797 | // Empties the buffers for transmission and reception 798 | // and reset counters 799 | s_panel_rx_status = 0; 800 | s_panel_rx_data_cnt = 0; 801 | SPI_RX_BUF_RESET(); 802 | SPI_TX_BUF_RESET(); 803 | 804 | // More on this below 805 | nts1_status_t res = s_spi_init(); 806 | 807 | if (res != k_nts1_status_ok) 808 | { 809 | return res; 810 | } 811 | 812 | // Sets the ACK pin to 1 813 | // More below 814 | s_port_startup_ack(); 815 | s_started = true; 816 | 817 | return k_nts1_status_ok; 818 | } 819 | 820 | // Called after a transaction is queued and ready for pickup by master. We use this to set the ACK line high. 821 | void s_spi_irq_handler_post_setup(spi_slave_transaction_t *trans) 822 | { 823 | s_port_startup_ack(); 824 | ready_transactions += 1; 825 | } 826 | 827 | // Called after transaction is sent/received. We use this to set the ACK line low. 828 | void s_spi_irq_handler_post_transaction(spi_slave_transaction_t *trans) 829 | { 830 | s_port_wait_ack(); 831 | } 832 | 833 | void s_ack_init() 834 | { 835 | // Configuration for the handshake line 836 | gpio_config_t io_conf = { 837 | .mode = GPIO_MODE_OUTPUT, 838 | .intr_type = GPIO_INTR_DISABLE, 839 | .pin_bit_mask = (1 << ACK_PIN)}; 840 | 841 | //Configure handshake line as output 842 | gpio_config(&io_conf); 843 | } 844 | 845 | nts1_status_t s_spi_init() 846 | { 847 | //Configuration for the SPI bus 848 | spi_bus_config_t buscfg = { 849 | .mosi_io_num = SPI_MOSI_PIN, 850 | .miso_io_num = SPI_MISO_PIN, 851 | .sclk_io_num = SPI_SCK_PIN, 852 | }; 853 | 854 | //Configuration for the SPI slave interface 855 | spi_slave_interface_config_t slvcfg = { 856 | .mode = SPI_MODE, 857 | .spics_io_num = SPI_CS_PIN, 858 | .queue_size = 0xFFF, 859 | .flags = SPI_BITORDER, 860 | .post_setup_cb = s_spi_irq_handler_post_setup, 861 | .post_trans_cb = s_spi_irq_handler_post_transaction, 862 | }; 863 | 864 | // Pull down on the Chip Select pin to make it always on 865 | gpio_set_pull_mode(SPI_CS_PIN, GPIO_PULLDOWN_ONLY); 866 | 867 | // Pull the SPI lines up (as per the schematics) 868 | gpio_set_pull_mode(SPI_SCK_PIN, GPIO_PULLUP_ONLY); // (CPOL = 1, normally high) 869 | gpio_set_pull_mode(SPI_MISO_PIN, GPIO_PULLUP_ONLY); 870 | gpio_set_pull_mode(SPI_MOSI_PIN, GPIO_PULLUP_ONLY); 871 | 872 | //Initialize SPI slave interface 873 | if (!spi_slave_initialize(S_SPI_HOST, &buscfg, &slvcfg, DMA_CHANNEL)) 874 | { 875 | return k_nts1_status_error; 876 | } 877 | 878 | s_spi_tx_buf_write(s_dummy_tx_cmd); 879 | s_spi_tx_buf_write(s_dummy_tx_cmd); 880 | s_spi_tx_buf_write(s_dummy_tx_cmd); 881 | s_spi_tx_buf_write(s_dummy_tx_cmd); 882 | 883 | return k_nts1_status_ok; 884 | } 885 | 886 | ``` 887 | 888 | It's all about setting up the SPI slave driver for the ESP32. You'll notice that the interrupt handlers don't do the heavy lifting as the do in the STM32. This is because the slave driver doesn't provide interrupts for the Tx FIFO empty or Rx FIFO not empty, as it does for the STM32. It just provides `transaction` events. So we're moving that part of the process to the `idle` handler instead, and only using the interrupts to change the ACK line. 889 | 890 | Note: there are low level interrupts that function similarly to the STM32's, but for the sake of getting something working I went with the simpler, driver provided interrupts. 891 | 892 | ### nts1_idle 893 | The idle handler sets up the SPI transactions, by moving the buffer pointers to the correct location, and allowing the SPI driver to write to them directly. 894 | 895 | ```c 896 | nts1_status_t nts1_idle() 897 | { 898 | uint8_t txdata; 899 | spi_slave_transaction_t transaction; 900 | transaction.length = SPI_TRANSACTION_BITS; 901 | uint8_t *tx_buf_ptr_first_byte = s_spi_tx_buf + s_spi_tx_ridx; 902 | 903 | // HOST <- PANEL 904 | if (!SPI_TX_BUF_EMPTY() && !s_spi_chk_tx_buf_space(SPI_TRANSACTION_BYTES)) 905 | { 906 | // If there's no space for the full transaction, reset the buffer 907 | SPI_TX_BUF_RESET(); 908 | } 909 | 910 | uint8_t tx_added_bytes_ctr = 0; 911 | for (; tx_added_bytes_ctr < SPI_TRANSACTION_BYTES; tx_added_bytes_ctr++) 912 | { 913 | // If there's no data to be sent in the Tx buffer, exit the loop 914 | if (SPI_TX_BUF_EMPTY()) 915 | break; 916 | 917 | // Save a pointer to the current read location on the Tx buffer 918 | uint8_t *tx_buf_ptr = s_spi_tx_buf + s_spi_tx_ridx; 919 | 920 | // read it and advance the read idx pointer 921 | txdata = s_spi_tx_buf_read(); 922 | 923 | // Check if the data is a Status message 924 | if (txdata & PANEL_START_BIT) 925 | { 926 | // Check if there is more data (after the status) to be sent next in the send buffer 927 | if (!SPI_TX_BUF_EMPTY()) 928 | { 929 | // Set the END_MARK on the status 930 | txdata |= PANEL_CMD_EMARK; 931 | // Note: this will set endmark on almost any status, especially those who have pending data, 932 | // which seems to contradict the endmark common usage of marking only the last command of a group 933 | } 934 | } 935 | 936 | // Save changes to the buffer 937 | *tx_buf_ptr = txdata; 938 | } 939 | 940 | // If we processed any bytes for the transaction 941 | if (tx_added_bytes_ctr) 942 | { 943 | // Point the transaction's Tx buffer to the first byte we want to send 944 | // from the software Tx buffer 945 | transaction.tx_buffer = (void *)tx_buf_ptr_first_byte; 946 | 947 | if (!s_spi_chk_rx_buf_space(SPI_TRANSACTION_BITS)) 948 | { 949 | // printf("resetting rx\n"); 950 | // If there's no space for the full transaction, reset the buffer 951 | SPI_RX_BUF_RESET(); 952 | } 953 | // Point the the transaction's Rx buffer to the current Rx write idx 954 | // It's important to do the casting to the result of the arithmetic, 955 | // if you leave the second parenthesis out, s_spi_rx_buf is converted 956 | // to a 32 bit pointer first, and then added s_spi_rx_widx positions 957 | // which results in writing beyond the buffer 958 | uint32_t *rx_buf_ptr = (uint32_t *) (s_spi_rx_buf + s_spi_rx_widx); 959 | // zero out the values; note that rx_buf_ptr points to 32 bits, not 8 960 | *rx_buf_ptr = 0; 961 | transaction.rx_buffer = (void *)rx_buf_ptr; 962 | 963 | // We always need to increase both counters by the length of the 964 | // transaction because the transaction will always read and write 965 | // transaction.length bits from the respective buffers 966 | for (uint8_t i = 0; i < SPI_TRANSACTION_BYTES; i++) 967 | { 968 | // Always increase Rx write counter 969 | s_spi_rx_widx = SPI_BUF_INC(s_spi_rx_widx, SPI_RX_BUF_SIZE); 970 | // Increase Tx read counter if we haven't already in the first loop 971 | if (i >= tx_added_bytes_ctr) 972 | { 973 | s_spi_tx_ridx = SPI_BUF_INC(s_spi_tx_ridx, SPI_TX_BUF_SIZE); 974 | } 975 | } 976 | 977 | esp_err_t err = spi_slave_queue_trans(S_SPI_HOST, &transaction, SPI_QUEUE_TTW); 978 | if(err){ 979 | // spi 0x3ffb2470 980 | // buff 0x3ffb270c 981 | } 982 | } 983 | 984 | if (ready_transactions) 985 | { 986 | // It's mandatory to call this function if using spi_slave_queue_trans 987 | spi_slave_transaction_t *out; 988 | spi_slave_get_trans_result(S_SPI_HOST, &out, SPI_QUEUE_TTW); 989 | ready_transactions -= 1; 990 | } 991 | 992 | // HOST I/F Give priority to Idle processing of received data 993 | // As long as the reception buffer is not empty 994 | while (!SPI_RX_BUF_EMPTY()) 995 | { 996 | // Reads from the buffer and executes the handler 997 | s_rx_msg_handler(s_spi_rx_buf_read()); 998 | } 999 | 1000 | return k_nts1_status_ok; 1001 | } 1002 | ``` 1003 | 1004 | ## Debugging and testing 1005 | When testing my changes, I got myself in a few weird places. Here are some noteworthy bits of wisdom acquired retrieved from those places: 1006 | - **Pay more attention to the reference schematic**: I had to do all the connections present in the schematics. I started only connecting the SPI and ACK pins, but I didn't get any messages back from the NTS1 this way. Finally connecting the LH6 pin to 3v3 with a pull up resistor got that working. 1007 | - **Be careful with pointer arithmetic**: at some point, I did pointer arithmetic to clear out some bits on the buffers. I wanted to clear out 32 bits using a uint8_t pointer, so I thought: "I'll cast the 8 bit pointer to a 32 bit one, like this `uint32_t *rx_buf_ptr = (uint32_t *) s_spi_rx_buf + s_spi_rx_widx;`, and Bob's your uncle... I was right, it worked, but I also didn't take into account that the casting happens **before** the sum. So I ended up clear up a memory beyond my intended position, which, obviously, broke things in unexpected ways. The fix was as silly as doing the math first, and then casting: `uint32_t *rx_buf_ptr = (uint32_t *) (s_spi_rx_buf + s_spi_rx_widx);` 1008 | - **Use a logic analyzer**: getting a logic analyzer paid off a thousandfold. The chinese clones, paired with Pulseview, are good enough and very cheap. It made it possible to see which lines were sending what. This got me over a few wiring issues quite nicely. 1009 | 1010 | ------ 1011 | 1012 | ## Next steps 1013 | - Fix the examples with the latest changes. 1014 | - Add some info on how I used Pulseview to debug the wiring. 1015 | - Add a proper diagram for the connections. 1016 | - Test more messages. 1017 | - Build an instrument with some sensors and a screen as a proof of concept. 1018 | 1019 | ----- 1020 | 1021 | # Notes for people not used to C 1022 | Like me. 1023 | 1024 | - `#define X`: this is a **preprocessor** macro, meaning that anything you define like this will not be stored in a variable. Instead the **preprocessor** will change references to it into the actual value in your code before compiling it. 1025 | - `int* pc; int c; c = 5; pc = &c;`: assign to `pc` the address of the variable `c`. 1026 | - `int* pc, c; c = 5; pc = &c; printf("%d", *pc);`: Output: 5. `*pc` derreferences (get the value) of the memory location pointed by `pc`. 1027 | - `int* pc, c; c = 5; pc = &c; *pc = 1; printf("%d", *pc); /* Ouptut: 1 */ printf("%d", c); // Output: 1`: you can write to the location pointed by a pointer by using derreference assignation. 1028 | - `int* pc = 0xFFu; int c; c = (int) pc; int* p2 = &c; printf("%x, %x", c, *p2) //Out: FF, FF`: You cannot store a memory address directly in a non-pointer variable, you need to cast it. 1029 | - `*((uint_32 int *) non_pointer_with_address) = value;`: Writing to the memory address stored in a non-pointer variable. 1030 | - `int p[] = {'a','b'}; p[1] == *(p + 1)`: An array is a pointer to a location in memory. 1031 | - `int * REG_GPIO_BASE = 0x0u; int REG_GPIO_SETTINGS; REG_GPIO[REG_GPIO_SETTINGS] |= (1u << 1)`: You can use array notation on a pointer to access offsets from a location in memory. 1032 | - `volatile int x`. Volatile variables tell the compiler that the value of the variable might change without the program making it so (eg: a register that contains the state of a gpio pin). This avoids compiler optimizations that might bypass storing the variable in memory, and thus break your code. 1033 | - Logic vs arithmetic (right) shifting: negative signed numbers (int8_t) behave differently from unsigned numbers (uint8_t) when performing bitwise right shifting. 1034 | - Logic right shifting: Unsigned numbers get logically shifted. `128 (10000000) >> 1` => `01000000`. 1035 | - Arithmetic right shifting: Signed negative numbers numbers get arithmetically shifted. `-64 (11000000) >> 1` => `11100000`. 1036 | - `(1u << 3)` => `00001000`: Single bit representation. 1037 | - `REG_X |= (1u << 3)`: sets the 4th bit (bit 3) of REG_X to 1. 1038 | - `REG_X &= ~(1u << 3)`: (un)sets the 4th bit (bit 3) of REG_X to 0. 1039 | - These last idioms are often optimized in the compiler into faster operations than doing the whole calculation, using a Bit Set or Bit Clear instructions instead of the operation, so it's preferrable to use them. 1040 | - Interrupts can happen between **processor** instructions. Meaning that even if you wrote a single statement, the processor can be still in the middle of excuting that statement when it gets interrupted, because your statement may get translated into several processor instructions. This is particularly important when setting bits in registers, as this often gets compiled into 3 instructions (or more). 1041 | - To avoid this happening when changing registers, you can use **atomic operations**. An assignment of a value that is equals or smaller (in bits) than the size of a single memory address, eg: 32bits for a a 32bit memory; is a atomic operation. This is not universal, C doesn't guarantee it. 1042 | -------------------------------------------------------------------------------- /docs/hex/hex-to-bin.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def to_bin(chars): 5 | scale = 16 # equals to hexadecimal 6 | num_of_bits = 8 7 | return bin(int(chars, scale))[2:].zfill(num_of_bits) 8 | 9 | 10 | input = sys.argv[1] 11 | if not input: 12 | exit(1) 13 | 14 | f = open(input, "r") 15 | hexfile = f.read() 16 | binfile = "" 17 | n = 2 18 | char_pairs = [hexfile[i:i+n] for i in range(0, len(hexfile), n)] 19 | 20 | for hexbyte in char_pairs: 21 | s = "\n\n" 22 | if hexbyte != "\n\n": 23 | in_binary = to_bin(hexbyte) 24 | s = "%s\n%s\n" % (hexbyte, in_binary) 25 | binfile += s 26 | 27 | out = open("%s.bin" % input, "w") 28 | out.write(binfile) 29 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/README.md: -------------------------------------------------------------------------------- 1 | ## NTS-1 2 | 3 | This library provides a communication interface to NTS-1 digital kit's main board. 4 | 5 | ### API Constants 6 | 7 | #### Success Statuses 8 | * **`NTS1::STATUS_OK`**: Operation completed without error 9 | * **`NTS1::STATUS_ERR`**: Operation failed with error 10 | * **`NTS1::STATUS_BUSY`**: Operation failed due to ongoing concurent operation 11 | * **`NTS1::STATUS_TIMEOUT`**: Operation timed out 12 | 13 | #### Transmittable Event IDs 14 | * **`NTS1::TX_EVENT_ID_NOTE_OFF`**: Note off event 15 | * **`NTS1::TX_EVENT_ID_NOTE_ON`**: Note on event 16 | * **`NTS1::TX_EVENT_ID_REQ_UNIT_COUNT`**: Request number of specified unit types 17 | * **`NTS1::TX_EVENT_ID_REQ_UNIT_DESC`**: Request descritptor for specified unit 18 | * **`NTS1::TX_EVENT_ID_REQ_EDIT_PARAM_DESC`**: Request descriptor for specified edit parameter 19 | * **`NTS1::TX_EVENT_ID_REQ_VALUE`**: Request current value for given parameter 20 | 21 | #### Receiveable Event IDs 22 | * **`NTS1::RX_EVENT_ID_NOTE_OFF`**: Note off event 23 | * **`NTS1::RX_EVENT_ID_NOTE_ON`**: Note on event 24 | * **`NTS1::RX_EVENT_ID_STEP_TICK`**: Step tick event 25 | * **`NTS1::RX_EVENT_ID_UNIT_DESC`**: Unit descriptor 26 | * **`NTS1::RX_EVENT_ID_EDIT_PARAM_DESC`**: Edit parameter descriptor 27 | * **`NTS1::RX_EVENT_ID_VALUE`**: Parameter value 28 | 29 | #### Main Parameter IDs 30 | ##### Oscillator 31 | * **`NTS1::PARAM_ID_OSC_TYPE`**: Oscillator type/index (0, num of osc.) 32 | * **`NTS1::PARAM_ID_OSC_SHAPE`**: Shape parameter (0, 1023) 33 | * **`NTS1::PARAM_ID_OSC_SHIFT_SHAPE`**: Alternate shape parameter (0, 1023) 34 | * **`NTS1::PARAM_ID_OSC_LFO_RATE`**: LFO rate (0, 1023) 35 | * **`NTS1::PARAM_ID_OSC_LFO_DEPTH`**: LFO depth (0, 1023) 36 | * **`NTS1::PARAM_ID_OSC_EDIT`**: Edit parameter when available, see PARAM_SUBID 37 | 38 | ##### Filter 39 | * **`NTS1::PARAM_ID_FILT_TYPE`**: Filter type/index (0, 6) 40 | * **`NTS1::PARAM_ID_FILT_CUTOFF`**: Filter cutoff frequency (0, 1023) 41 | * **`NTS1::PARAM_ID_FILT_PEAK`**: Filter resonance (0, 1023) 42 | * **`NTS1::PARAM_ID_FILT_LFO_RATE`**: Sweep rate (0, 1023) 43 | * **`NTS1::PARAM_ID_FILT_LFO_DEPTH`**: Sweep depth (0, 1023) 44 | 45 | ##### Amplitude E.G. 46 | * **`NTS1::PARAM_ID_AMPEG_TYPE`**: Envelope type/index (0, 4) 47 | * **`NTS1::PARAM_ID_AMPEG_ATTACK`**: Envelope attack time (0, 1023) 48 | * **`NTS1::PARAM_ID_AMPEG_RELEASE`**: Envelope release time (0, 1023) 49 | * **`NTS1::PARAM_ID_AMPEG_LFO_RATE`**: Tremollo rate (0, 1023) 50 | * **`NTS1::PARAM_ID_AMPEG_LFO_DEPTH`**: Tremollo depth (0, 1023) 51 | 52 | ##### Modulation Effect 53 | * **`NTS1::PARAM_ID_MOD_TYPE`**: Modulation effect type/index (0, num of mod. fx) 54 | * **`NTS1::PARAM_ID_MOD_TIME`**: Time parameter (0, 1023) 55 | * **`NTS1::PARAM_ID_MOD_DEPTH`**: Depth parameter (0, 1023) 56 | 57 | ##### Delay Effect 58 | * **`NTS1::PARAM_ID_DEL_TYPE`**: Delay effect type/index (0, num of del. fx) 59 | * **`NTS1::PARAM_ID_DEL_TIME`**: Time parameter (0, 1023) 60 | * **`NTS1::PARAM_ID_DEL_DEPTH`**: Depth parameter (0, 1023) 61 | * **`NTS1::PARAM_ID_DEL_MIX`**: Wet/dry mix parameter (0, 1023) 62 | 63 | ##### Reverb Effect 64 | * **`NTS1::PARAM_ID_REV_TYPE`**: Reverb effect type/index (0, num of rev. fx) 65 | * **`NTS1::PARAM_ID_REV_TIME`**: Time parameter (0, 1023) 66 | * **`NTS1::PARAM_ID_REV_DEPTH`**: Depth parameter (0, 1023) 67 | * **`NTS1::PARAM_ID_REV_MIX`**: Wet/dry mix parameter (0, 1023) 68 | 69 | ##### Arpeggiator 70 | * **`NTS1::PARAM_ID_ARP_PATTERN`**: Arpeggiator pattern 71 | * **`NTS1::PARAM_ID_ARP_INTERVALS`**: Arpeggiator note intervals 72 | * **`NTS1::PARAM_ID_ARP_LENGTH`**: Arpeggiator sequence length 73 | * **`NTS1::PARAM_ID_ARP_STATE`**: Current arpeggiator state 74 | * **`NTS1::PARAM_ID_ARP_TEMPO`**: Arpeggiator tempo 75 | 76 | ##### Meta Parameters 77 | * **`NTS1::PARAM_ID_SYS_VERSION`**: Current main board system version. 78 | * **`NTS1::PARAM_ID_SYS_GLOBAL`**: Global options, see corresponding sub-ID. 79 | * **`NTS1::PARAM_ID_INVALID`**: Invalid parameter ID 80 | 81 | #### Sub Parameter IDs 82 | ##### Oscillator 83 | * **`NTS1::PARAM_SUBID_OSC_EDIT1`** 84 | * **`NTS1::PARAM_SUBID_OSC_EDIT2`** 85 | * **`NTS1::PARAM_SUBID_OSC_EDIT3`** 86 | * **`NTS1::PARAM_SUBID_OSC_EDIT4`** 87 | * **`NTS1::PARAM_SUBID_OSC_EDIT5`** 88 | * **`NTS1::PARAM_SUBID_OSC_EDIT6`** 89 | 90 | ##### Global Options 91 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_INPUT_ROUTE`** 92 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_INPUT_TRIM`** 93 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_SYNCOUT_POLARITY`** 94 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_SYNCIN_POLARITY`** 95 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_TEMPO_RANGE`** 96 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_CLOCK_SOURCE`** 97 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_SHORT_MESSAGE`** 98 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_MIDI_ROUTE`** 99 | * **`NTS1::PARAM_SUBID_SYS_GLOBAL_SYNC_STEP`** 100 | 101 | ##### Other 102 | * **`NTS1::INVALID_PARAM_SUBID`**: 103 | 104 | #### Parameter Type 105 | * **`NTS1::EDIT_PARAM_TYPE_PERCENT`** 106 | * **`NTS1::EDIT_PARAM_TYPE_PERCENT_BIPOLAR`** 107 | * **`NTS1::EDIT_PARAM_TYPE_SELECT`** 108 | 109 | ### API Data Types 110 | 111 | #### Handlers for Received Messages 112 | 113 | * **`typedef void (*nts1_note_off_event_handler)(const nts1_rx_note_off_t *)`** 114 | * **`typedef void (*nts1_note_on_event_handler)(const nts1_rx_note_on_t *)`** 115 | * **`typedef void (*nts1_step_tick_event_handler)(void)`** 116 | * **`typedef void (*nts1_unit_desc_event_handler)(const nts1_rx_unit_desc_t *)`** 117 | * **`typedef void (*nts1_edit_param_desc_event_handler)(const nts1_rx_edit_param_desc_t *)`** 118 | * **`typedef void (*nts1_value_event_handler)(const nts1_rx_value_t *)`** 119 | * **`typedef void (*nts1_param_change_handler)(const nts1_rx_param_change_t *)`** 120 | 121 | #### Receiveable Types 122 | 123 | * **`nts1_rx_param_change_t`** 124 | 125 | ``` 126 | typedef struct nts1_rx_param_change { 127 | uint8_t param_id; 128 | uint8_t param_subid; 129 | uint8_t msb; // 7 bit 130 | uint8_t lsb; // 7 bit 131 | } nts1_rx_param_change_t; 132 | ``` 133 | 134 | * **`nts1_rx_note_off_t`** 135 | 136 | ``` 137 | typedef struct { 138 | uint8_t note; 139 | uint8_t padding; 140 | } nts1_rx_note_off_t; 141 | ``` 142 | 143 | * **`nts1_rx_note_on_t`** 144 | 145 | ``` 146 | typedef struct { 147 | uint8_t note; 148 | uint8_t velocity; 149 | } nts1_rx_note_on_t; 150 | ``` 151 | 152 | * **`nts1_rx_value_t`** 153 | 154 | ``` 155 | typedef struct { 156 | uint8_t req_id; 157 | uint8_t main_id; 158 | uint8_t sub_id; 159 | uint8_t padding; 160 | uint16_t value; 161 | uint8_t padding2[10]; 162 | } nts1_rx_value_t; 163 | ``` 164 | 165 | * **`nts1_rx_unit_desc_t`** 166 | 167 | ``` 168 | typedef struct { 169 | uint8_t main_id; 170 | uint8_t sub_id; 171 | uint8_t param_count; 172 | char name[13]; 173 | } nts1_rx_unit_desc_t; 174 | ``` 175 | 176 | * **`nts1_rx_edit_param_desc_t`** 177 | 178 | ``` 179 | typedef struct { 180 | uint8_t main_id; 181 | uint8_t sub_id; 182 | uint8_t value_type; 183 | int8_t min; 184 | int8_t max; 185 | char name[13]; 186 | } nts1_rx_edit_param_desc_t; 187 | ``` 188 | 189 | ### API Functions 190 | 191 | * **`NTS1::NTS(void)`**: Default class constructor 192 | 193 | * **`void NTS1::init(void)`**: Initialialize interface to main board 194 | 195 | * **`void NTS1::idle(void)`**: Process tx/rx communications with main board 196 | 197 | #### Direct Messages 198 | 199 | * **`uint8_t NTS1::paramChange(uint8_t id, uint8_t subid, uint16_t value)`**: Send a parameter change message 200 | _Params_ Parameter id 201 | _Params_ Parameter sub-id 202 | _Params_ Value 203 | _Returns_ Sucess status 204 | 205 | * **`uint8_t NTS1::noteOn(uint8_t note, uint8_t velo)`**: Send a a note on event 206 | _Params_ Note (0-127 per MIDI interpretation) 207 | _Params_ Velocity 208 | _Returns_ Sucess status 209 | 210 | * **`uint8_t NTS1::noteOff(uint8_t note)`**: Send a a note off event 211 | _Params_ Note (0-127 per MIDI interpretation) 212 | _Returns_ Sucess status 213 | 214 | #### Requests 215 | 216 | * **`uint8_t NTS1::reqSysVersion(void)`**: Request main board system version 217 | _Returns_ Sucess status 218 | 219 | * **`uint8_t NTS1::reqParamValue(uint8_t id, uint8_t subid)`**: Request value for specified parameter 220 | _Params_ param ID 221 | _Params_ param sub-ID 222 | _Returns_ Sucess status 223 | 224 | * **`uint8_t NTS1::reqOscCount(void)`**: Request number of oscillators 225 | _Returns_ Sucess status 226 | 227 | * **`uint8_t NTS1::reqOscDesc(uint8_t idx)`**: Request oscillator descriptor 228 | _Params_ Index 229 | _Returns_ Sucess status 230 | 231 | * **`uint8_t NTS1::reqOscEditParamDesc(uint8_t idx)`**: Request oscillator edit parameter descriptor 232 | _Returns_ Sucess status 233 | 234 | * **`uint8_t NTS1::reqFilterCount(void)`**: Request number of filters 235 | _Returns_ Sucess status 236 | 237 | * **`uint8_t NTS1::reqFilterDesc(uint8_t idx)`**: Request filter descriptor 238 | _Params_ Index 239 | _Returns_ Sucess status 240 | 241 | * **`uint8_t NTS1::reqAmpEGCount(void)`**: Request number of amplitude env. generators 242 | _Returns_ Sucess status 243 | 244 | * **`uint8_t NTS1::reqAmpEGDesc(uint8_t idx)`**: Request amplitude env. generator descriptor 245 | _Params_ Index 246 | _Returns_ Sucess status 247 | 248 | * **`uint8_t NTS1::reqModCount(void)`**: Request number of modulation effects 249 | _Returns_ Sucess status 250 | 251 | * **`uint8_t NTS1::reqModDesc(uint8_t idx)`**: Request modulation effect descriptor 252 | _Params_ Index 253 | _Returns_ Sucess status 254 | 255 | * **`uint8_t NTS1::reqDelayCount(void)`**: Request number of delay effects 256 | _Returns_ Sucess status 257 | 258 | * **`uint8_t NTS1::reqDelayDesc(uint8_t idx)`**: Request delay effect descriptor 259 | _Params_ Index 260 | _Returns_ Sucess status 261 | 262 | * **`uint8_t NTS1::reqReverbCount(void)`**: Request number of reverb effects 263 | _Returns_ Sucess status 264 | 265 | * **`uint8_t NTS1::reqReverbDesc(uint8_t idx)`**: Request reverb effect descriptor 266 | _Params_ Index 267 | _Returns_ Sucess status 268 | 269 | * **`uint8_t NTS1::reqArpPatternCount(void)`**: Request number of arpeggiator patterns 270 | _Returns_ Sucess status 271 | 272 | * **`uint8_t NTS1::reqArpPatternDesc(uint8_t idx)`**: Request arpeggiator pattern descriptor 273 | _Params_ Index 274 | _Returns_ Sucess status 275 | 276 | * **`uint8_t NTS1::reqArpIntervalsCount(void)`**: Request number of arpeggiator intervals 277 | _Returns_ Sucess status 278 | 279 | * **`uint8_t NTS1::reqArpIntervalsDesc(uint8_t idx)`**: Request arpeggiator intervals descriptor 280 | _Params_ Index 281 | _Returns_ Sucess status 282 | 283 | 284 | 285 | #### Message Handlers 286 | 287 | * **`void NTS1::setParamChangeHandler(nts1_param_change_handler handler)`**: Set handler function for param change messages 288 | _Params_ Handler function 289 | 290 | * **`void NTS1::setNoteOnEventHandler(nts1_note_on_event_handler handler)`**: Set handler function for note on events 291 | _Params_ Handler function 292 | 293 | * **`void NTS1::setNoteOffEventHandler(nts1_note_off_event_handler handler)`**: Set handler function for note off events 294 | _Params_ Handler function 295 | 296 | * **`void NTS1::setStepTickEventHandler(nts1_step_tick_event_handler handler)`**: Set handler function for step tick events 297 | _Params_ Handler function 298 | 299 | * **`void NTS1::setUnitDescEventHandler(nts1_unit_desc_event_handler handler)`**: Set handler function for unit descriptor replies 300 | _Params_ Handler function 301 | 302 | * **`void NTS1::setEditParamDescEventHandler(nts1_edit_param_desc_event_handler handler)`**: Set handler function for edit parameter descriptor replies 303 | _Params_ Handler function 304 | 305 | * **`void NTS1::setValueEventHandler(nts1_value_event_handler handler)`**: Set handler function for value replies 306 | _Params_ Handler function 307 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/examples/Blank_Template/Blank_Template.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Blank Template for NTS-1 Custom Panel reference board rev.B 3 | 4 | This is free and unencumbered software released into the public domain. 5 | 6 | Anyone is free to copy, modify, publish, use, compile, sell, or 7 | distribute this software, either in source code form or as a compiled 8 | binary, for any purpose, commercial or non-commercial, and by any 9 | means. 10 | 11 | In jurisdictions that recognize copyright laws, the author or authors 12 | of this software dedicate any and all copyright interest in the 13 | software to the public domain. We make this dedication for the benefit 14 | of the public at large and to the detriment of our heirs and 15 | successors. We intend this dedication to be an overt act of 16 | relinquishment in perpetuity of all present and future rights to this 17 | software under copyright law. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | For more information, please refer to 28 | //*/ 29 | 30 | // --- NTS-1 main board interface ------------------------------------------------------------------ 31 | #include 32 | 33 | NTS1 nts1; 34 | 35 | // --- Arduino Sketch Entry ------------------------------------------------------------------------ 36 | 37 | void setup() { 38 | nts1.init(); 39 | 40 | // put your setup code here, to run once: 41 | 42 | } 43 | 44 | // --- Arduino Sketch Idle Loop ---------------------------------------------------------------------- 45 | 46 | void loop() { 47 | nts1.idle(); 48 | 49 | // put your main code here, to run repeatedly: 50 | 51 | } 52 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/examples/Blank_Template/README.md: -------------------------------------------------------------------------------- 1 | ## Blank Template 2 | 3 | This template Arduino Sketch contains the bare minimum required to build a project for the NTS-1 Custom Panel reference board: 4 | 5 | ``` 6 | #include 7 | 8 | NTS1 nts1; 9 | 10 | void setup() { 11 | nts1.init(); // Performs hardware and peripheral initialization 12 | 13 | // Insert your setup code here 14 | } 15 | 16 | void loop() { 17 | nts1.idle(); // Runs internal processing of tx/rx communication with the main board 18 | 19 | // Insert your loop code here 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/examples/Sequencer_Template/README.md: -------------------------------------------------------------------------------- 1 | ## Sequencer Template 2 | 3 | This template demonstrates how to use the NTS1 Class to trigger note on/off events and set parameters on the NTS-1 digital kit main board. It also demonstrates how to configure hardware timers to generate interrupts to drive time sensitive operations like switch/rotary scanning and sequencer ticks. 4 | 5 | * LEDs 1 to 8 are used to display the on/off state of each 8 step of the sequence 6 | * Pressing switches 1 to 8 while holding down switch 10 toggles the on/off state of that step in the sequence 7 | * Rotary 1/A allows to change the oscillator SHAPE parameter 8 | * Turning rotary 1/A while holding down switches 1 to 8 changes the pitch for those steps 9 | * Turning rotary 1/A while holding switch 10 changes the sequencer's tempo 10 | * Switch 9 start and stops the sequencer 11 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/examples/Sequencer_Template/Sequencer_Template.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Sequencer Template for NTS-1 Custom Panel reference board rev.B 3 | 4 | BSD 3-Clause License 5 | Copyright (c) 2020, KORG INC. 6 | All rights reserved. 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | //*/ 28 | 29 | // --- NTS-1 main boardinterface ------------------------------------------------------------------ 30 | #include 31 | 32 | NTS1 nts1; 33 | 34 | // --- UI definitions and state ------------------------------------------------------------------- 35 | enum { sw_1 = 0, sw_2, sw_3, sw_4, sw_5, sw_6, sw_7, sw_8, sw_9, sw_10, sw_count }; 36 | const uint8_t g_sw_pins[sw_count] = { D34, D35, D36, D37, D38, D40, D41, D42, D46, D47}; 37 | 38 | enum { 39 | sw_step1 = sw_1, 40 | sw_step2 = sw_2, 41 | sw_step3 = sw_3, 42 | sw_step4 = sw_4, 43 | sw_step5 = sw_5, 44 | sw_step6 = sw_6, 45 | sw_step7 = sw_7, 46 | sw_step8 = sw_8, 47 | sw_play = sw_9, 48 | sw_shift = sw_10, 49 | }; 50 | 51 | enum { led_1 = 0, led_2, led_3, led_4, led_5, led_6, led_7, led_8, led_count }; 52 | const uint8_t g_led_pins[led_count] = { D16, D17, D18, D19, D22, D23, D24, D25 }; 53 | 54 | enum { vr_1 = 0, vr_count }; 55 | const uint8_t g_vr_pins[vr_count] = { A10 }; 56 | 57 | typedef struct { 58 | HardwareTimer *timer; // for switch/vr scan 59 | uint32_t steps_pressed; 60 | bool is_shift_pressed; 61 | } ui_state_t; 62 | 63 | ui_state_t g_ui_state = { 64 | .timer = NULL, 65 | .steps_pressed = 0x0, 66 | .is_shift_pressed = false 67 | }; 68 | 69 | // --- Sequencer definitions and state ----------------------------------------------------------- 70 | 71 | #define k_seq_length 8 72 | #define k_seq_ticks_per_step 100 73 | 74 | enum { 75 | k_seq_flag_reset = 1U<<0 76 | }; 77 | 78 | typedef struct { 79 | HardwareTimer *timer; // for seq ticks 80 | uint32_t last_tick_us; 81 | uint8_t ticks; 82 | uint8_t step; 83 | uint8_t note; 84 | uint8_t flags; 85 | uint32_t gates; 86 | uint32_t tempo; 87 | uint8_t notes[k_seq_length]; 88 | bool is_playing; 89 | } seq_state_t; 90 | 91 | seq_state_t g_seq_state = { 92 | .timer = NULL, 93 | .last_tick_us = 0, 94 | .ticks = 0xFF, // invalid 95 | .step = 0xFF, // invalid 96 | .note = 0xFF, // invalid 97 | .flags = 0x00, // none 98 | .gates = 0xFF, // 1 bit per step, all on 99 | .tempo = 1200, // 120.0 x 10 100 | .notes = { 101 | 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42 102 | }, 103 | .is_playing = false 104 | }; 105 | 106 | // --- UI scan/control ------------------------------------------------------------------------ 107 | 108 | void set_step_leds(uint8_t mask) { 109 | for (uint8_t i=led_1; i<=led_8; ++i) { 110 | nts1_digital_write(g_led_pins[i], (mask & (1U< 1000) { 121 | uint32_t sw_state = 0; 122 | for (uint8_t i=0; i>i)&0x1); 126 | if (val != last_val) { 127 | if (++sw_chatter_cnt[i] > 6) { 128 | sw_state |= val<>sw_step1; 176 | set_step_leds(g_seq_state.gates); 177 | } 178 | 179 | g_ui_state.steps_pressed |= new_presses; 180 | g_ui_state.steps_pressed &= ~(released); 181 | } 182 | } 183 | 184 | last_sw_state = sw_state; // save state for ref. on next cycle 185 | last_sw_sample_us = now_us; 186 | } 187 | } 188 | 189 | void handle_vr_1(int16_t value) { 190 | // Keep references for catch mode behavior 191 | static int32_t last_tempo_vr_val = 0xFFFFFFFF; 192 | static int32_t last_shape_vr_val = 0xFFFFFFFF; 193 | 194 | if (g_ui_state.steps_pressed) { 195 | // Set note if steps are pressed 196 | uint8_t note = value>>3; // 10bit to 7bit 197 | for (uint8_t i=sw_step1; i<=sw_step8; ++i) { 198 | if (g_ui_state.steps_pressed & (1U<>1)*10 + (value&0x1)*5; // 20.0 - 531.5 BPM in 0.5 increments 218 | last_tempo_vr_val = value; 219 | } 220 | } 221 | else { 222 | // Change SHAPE by default 223 | if(last_tempo_vr_val == 0xFFFFFFFF 224 | || (abs(value - last_shape_vr_val) < 10)) { 225 | // Set SHAPE parameter on NTS-1 main board 226 | nts1.paramChange(k_param_id_osc_shape, k_invalid_param_subid, value); 227 | last_shape_vr_val = value; 228 | } 229 | } 230 | } 231 | 232 | void scan_vrs(unsigned long now_us) { 233 | static uint32_t last_vr_sample_us = 0; 234 | static uint32_t last_vr_states[vr_count] = {0}; 235 | static uint8_t vr_chatter_cnt[vr_count] = {0}; 236 | 237 | if (now_us - last_vr_sample_us > 1000) { 238 | for (uint8_t i=0; i 4) { 245 | vr_state = val; 246 | vr_chatter_cnt[i] = 0; 247 | } 248 | else { 249 | vr_state = last_val; 250 | } 251 | } 252 | else { 253 | vr_chatter_cnt[i] = 0; 254 | vr_state = last_val; 255 | } 256 | 257 | if (last_val == vr_state) { 258 | last_vr_sample_us = now_us; 259 | continue; 260 | } 261 | 262 | switch (i) { 263 | case vr_1: 264 | handle_vr_1(vr_state & 0x3FF); 265 | break; 266 | default: 267 | break; 268 | } 269 | 270 | last_vr_states[i] = vr_state; 271 | } 272 | 273 | last_vr_sample_us = now_us; 274 | } 275 | } 276 | 277 | void scan_interrupt_handler(HardwareTimer *t) { 278 | unsigned long now_us = micros(); 279 | scan_switches(now_us); 280 | scan_vrs(now_us); 281 | } 282 | 283 | // --- Sequencer Runtime ------------------------------------------------------------------------ 284 | 285 | void seq_interrupt_handler(HardwareTimer *t) { 286 | uint32_t now_us = micros(); 287 | 288 | if (g_seq_state.flags & k_seq_flag_reset) { 289 | if (g_seq_state.note != 0xFF) { 290 | // There's a pending note on 291 | // Send note off event to NTS-1 main board 292 | nts1.noteOff(g_seq_state.note); 293 | const uint32_t cur_step = g_seq_state.step; 294 | const uint8_t highlow = (g_seq_state.gates & (1U<= k_seq_ticks_per_step) { 323 | cur_step = (cur_step + 1) % k_seq_length; 324 | g_seq_state.ticks = 0; 325 | 326 | const uint8_t note = g_seq_state.notes[cur_step]; 327 | 328 | if (g_seq_state.gates & (1U<= (k_seq_ticks_per_step>>1)) { 341 | if (g_seq_state.note != 0xFF) { 342 | // Send note off event to NTS-1 main board 343 | nts1.noteOff(g_seq_state.note); 344 | uint32_t cur_step = g_seq_state.step; 345 | const uint8_t highlow = (g_seq_state.gates & (1U<setMode(channel, TIMER_OUTPUT_COMPARE); 360 | 361 | // With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX 362 | // = (SystemCoreClock) / 65535 363 | // Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz 364 | // To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. 365 | // The maximum frequency depends on processing of the interruption and thus depend on board used 366 | // Example on Nucleo_L476RG with systemClock at 80MHz the interruption processing is around 4,5 microseconds and thus Max frequency is around 220kHz 367 | const uint32_t PrescalerFactor = 4; 368 | timer->setPrescaleFactor(PrescalerFactor); 369 | timer->setOverflow(overflw); //0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover 370 | timer->attachInterrupt(channel, interrupt_handler); 371 | //uint32_t freq = timer->getTimerClkFreq() / timer->getPrescaleFactor(); 372 | return timer; 373 | } 374 | 375 | // --- Arduino Sketch Entry ------------------------------------------------------------------------ 376 | 377 | void setup() { 378 | // - Setup debug serial if needed ------------------------------ 379 | Serial.begin(115200); 380 | 381 | // - Initialize VR and LED GPIOS ------------------------------- 382 | for (uint8_t i=0; i 500ns per cycle 401 | 402 | // - Start timers ---------------------------------------------- 403 | g_ui_state.timer->resume(); 404 | g_seq_state.timer->resume(); 405 | } 406 | 407 | // --- Arduino Sketch Idle Loop ---------------------------------------------------------------------- 408 | 409 | void loop() { 410 | unsigned long now_ms = millis(); 411 | unsigned long now_us = micros(); 412 | 413 | // - Run NTS-1 interface idle callback ------------------------- 414 | nts1.idle(); 415 | } 416 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For NTS-1 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | NTS1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | ####################################### 16 | # Instances (KEYWORD2) 17 | ####################################### 18 | 19 | ####################################### 20 | # Constants (LITERAL1) 21 | ####################################### 22 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/library.properties: -------------------------------------------------------------------------------- 1 | name=NTS-1 2 | version=1.0 3 | author=KORGInc 4 | maintainer=KORGInc 5 | sentence=NTS-1 digital kit main board interface 6 | paragraph= 7 | category=Communication 8 | url=https://github.com/korginc/nts-1-customizations/tree/master/Custom_Panel_RevC/Arduino/libraries/NTS-1 9 | architectures=stm32 10 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/src/nts-1.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nts-1.cpp 3 | * @brief NTS1 Class implementation. 4 | * 5 | * Provides an object oriented interface for the C API 6 | * 7 | * BSD 3-Clause License 8 | * Copyright (c) 2020, KORG INC. 9 | * All rights reserved. 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * * Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * * Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | //*/ 31 | 32 | #include "nts-1.h" 33 | 34 | static NTS1 *sNts1Instance = nullptr; 35 | 36 | static nts1_note_off_event_handler sNoteOffEventHandler = nullptr; 37 | static nts1_note_on_event_handler sNoteOnEventHandler = nullptr; 38 | static nts1_step_tick_event_handler sStepTickEventHandler = nullptr; 39 | static nts1_unit_desc_event_handler sUnitDescEventHandler = nullptr; 40 | static nts1_edit_param_desc_event_handler sEditParamDescEventHandler = nullptr; 41 | static nts1_value_event_handler sValueEventHandler = nullptr; 42 | static nts1_param_change_handler sParamChangeHandler = nullptr; 43 | 44 | 45 | NTS1::NTS1(void) 46 | { 47 | sNts1Instance = this; 48 | } 49 | 50 | NTS1::~NTS1(void) { 51 | if (sNts1Instance == this) 52 | sNts1Instance = nullptr; 53 | } 54 | 55 | void NTS1::setNoteOffEventHandler(nts1_note_off_event_handler handler) { 56 | sNoteOffEventHandler = handler; 57 | } 58 | 59 | void NTS1::setNoteOnEventHandler(nts1_note_on_event_handler handler) { 60 | sNoteOnEventHandler = handler; 61 | } 62 | 63 | void NTS1::setStepTickEventHandler(nts1_step_tick_event_handler handler) { 64 | sStepTickEventHandler = handler; 65 | } 66 | 67 | void NTS1::setUnitDescEventHandler(nts1_unit_desc_event_handler handler) { 68 | sUnitDescEventHandler = handler; 69 | } 70 | 71 | void NTS1::setEditParamDescEventHandler(nts1_edit_param_desc_event_handler handler) { 72 | sEditParamDescEventHandler = handler; 73 | } 74 | 75 | void NTS1::setValueEventHandler(nts1_value_event_handler handler) { 76 | sValueEventHandler = handler; 77 | } 78 | 79 | void NTS1::setParamChangeHandler(nts1_param_change_handler handler) { 80 | sParamChangeHandler = handler; 81 | } 82 | 83 | extern "C" __attribute__((weak)) 84 | void nts1_handle_note_off_event(const nts1_rx_note_off_t *note_off) { 85 | if (sNoteOffEventHandler != nullptr) { 86 | sNoteOffEventHandler(note_off); 87 | } 88 | } 89 | 90 | extern "C" __attribute__((weak)) 91 | void nts1_handle_note_on_event(const nts1_rx_note_on_t *note_on) { 92 | if (sNoteOnEventHandler != nullptr) { 93 | sNoteOnEventHandler(note_on); 94 | } 95 | } 96 | 97 | extern "C" __attribute__((weak)) 98 | void nts1_handle_step_tick_event(void) { 99 | if (sStepTickEventHandler != nullptr) { 100 | sStepTickEventHandler(); 101 | } 102 | } 103 | 104 | extern "C" __attribute__((weak)) 105 | void nts1_handle_unit_desc_event(const nts1_rx_unit_desc_t *unit_desc) { 106 | if (sUnitDescEventHandler != nullptr) { 107 | sUnitDescEventHandler(unit_desc); 108 | } 109 | } 110 | 111 | extern "C" __attribute__((weak)) 112 | void nts1_handle_edit_param_desc_event(const nts1_rx_edit_param_desc_t *param_desc) { 113 | if (sEditParamDescEventHandler != nullptr) { 114 | sEditParamDescEventHandler(param_desc); 115 | } 116 | } 117 | 118 | extern "C" __attribute__((weak)) 119 | void nts1_handle_value_event(const nts1_rx_value_t *value) { 120 | if (sValueEventHandler != nullptr) { 121 | sValueEventHandler(value); 122 | } 123 | } 124 | 125 | extern "C" __attribute__((weak)) 126 | void nts1_handle_param_change(const nts1_rx_param_change_t *param_change) { 127 | if (sParamChangeHandler != nullptr) { 128 | sParamChangeHandler(param_change); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/libraries/NTS-1/src/nts-1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nts-1.h 3 | * @brief NTS1 Class definition. 4 | * 5 | * Provides an object oriented interface for the C API 6 | * 7 | * BSD 3-Clause License 8 | * Copyright (c) 2020, KORG INC. 9 | * All rights reserved. 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * * Redistributions of source code must retain the above copyright notice, this 13 | * list of conditions and the following disclaimer. 14 | * * Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * * Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | //*/ 31 | 32 | #ifndef _NTS1_H_ 33 | #define _NTS1_H_ 34 | 35 | #include "nts1_iface.h" 36 | 37 | class NTS1 { 38 | public: 39 | 40 | /** 41 | * Default constructor 42 | */ 43 | NTS1(void); 44 | 45 | /** 46 | * Default destructor 47 | */ 48 | ~NTS1(void); 49 | 50 | // ---------------------------------------------------------- 51 | 52 | /** 53 | * Success status returned by various operations 54 | */ 55 | enum { 56 | STATUS_OK = 0x00U, 57 | STATUS_ERR = 0x01U, 58 | STATUS_BUSY = 0x02U, 59 | STATUS_TIMEOUT = 0x03U, 60 | }; 61 | 62 | /** 63 | * Transmitable event IDs 64 | */ 65 | enum { 66 | TX_EVENT_ID_NOTE_OFF = 0x00U, 67 | TX_EVENT_ID_NOTE_ON = 0x01U, 68 | TX_EVENT_ID_INC_PARAM = 0x04U, 69 | TX_EVENT_ID_INC_OSC_PARAM = 0x05U, 70 | TX_EVENT_ID_REQ_UNIT_COUNT = 0x10U, 71 | TX_EVENT_ID_REQ_UNIT_DESC = 0x11U, 72 | TX_EVENT_ID_REQ_PARAM_DESC = 0x12U, 73 | TX_EVENT_ID_REQ_VALUE = 0x13U, 74 | }; 75 | 76 | /** 77 | * Receivable event IDs 78 | */ 79 | enum { 80 | RX_EVENT_ID_NOTE_OFF = 0x0U, 81 | RX_EVENT_ID_NOTE_ON = 0x1U, 82 | RX_EVENT_ID_STEP_TICK = 0x2U, 83 | RX_EVENT_ID_UNIT_DESC = 0x11U, 84 | RX_EVENT_ID_PARAM_DESC = 0x12U, 85 | RX_EVENT_ID_VALUE = 0x13U, 86 | }; 87 | 88 | /** 89 | * Parameter IDs 90 | */ 91 | enum { 92 | // OSC 93 | PARAM_ID_OSC_BASE = 0U, 94 | PARAM_ID_OSC_TYPE = PARAM_ID_OSC_BASE, 95 | PARAM_ID_OSC_SHAPE, 96 | PARAM_ID_OSC_SHIFT_SHAPE, 97 | PARAM_ID_OSC_LFO_RATE, 98 | PARAM_ID_OSC_LFO_DEPTH, 99 | PARAM_ID_OSC_EDIT, // WITH SUBID 100 | PARAM_ID_OSC_LAST = PARAM_ID_OSC_EDIT, 101 | 102 | // AMPEG 103 | PARAM_ID_AMPEG_BASE, 104 | PARAM_ID_AMPEG_TYPE = PARAM_ID_AMPEG_BASE, 105 | PARAM_ID_AMPEG_ATTACK, 106 | PARAM_ID_AMPEG_RELEASE, 107 | PARAM_ID_AMPEG_LFO_RATE, 108 | PARAM_ID_AMPEG_LFO_DEPTH, 109 | PARAM_ID_AMPEG_RESERVED3, 110 | PARAM_ID_AMPEG_LAST = PARAM_ID_AMPEG_RESERVED3, 111 | 112 | // FILTER 113 | PARAM_ID_FILT_BASE, 114 | PARAM_ID_FILT_TYPE = PARAM_ID_FILT_BASE, 115 | PARAM_ID_FILT_CUTOFF, 116 | PARAM_ID_FILT_PEAK, 117 | PARAM_ID_FILT_LFO_RATE, 118 | PARAM_ID_FILT_LFO_DEPTH, 119 | PARAM_ID_FILT_RESERVED3, 120 | PARAM_ID_FILT_LAST = PARAM_ID_FILT_RESERVED3, 121 | 122 | // MODULATION EFFECT 123 | PARAM_ID_MOD_BASE, 124 | PARAM_ID_MOD_TYPE = PARAM_ID_MOD_BASE, 125 | PARAM_ID_MOD_TIME, 126 | PARAM_ID_MOD_DEPTH, 127 | PARAM_ID_MOD_RESERVED1, 128 | PARAM_ID_MOD_RESERVED2, 129 | PARAM_ID_MOD_RESERVED3, 130 | PARAM_ID_MOD_LAST = PARAM_ID_MOD_RESERVED3, 131 | 132 | // DELAY EFFECT 133 | PARAM_ID_DEL_BASE, 134 | PARAM_ID_DEL_TYPE = PARAM_ID_DEL_BASE, 135 | PARAM_ID_DEL_TIME, 136 | PARAM_ID_DEL_DEPTH, 137 | PARAM_ID_DEL_RESERVED1, 138 | PARAM_ID_DEL_MIX, 139 | PARAM_ID_DEL_RESERVED3, 140 | PARAM_ID_DEL_LAST = PARAM_ID_DEL_RESERVED3, 141 | 142 | // REVERB EFFECT 143 | PARAM_ID_REV_BASE, 144 | PARAM_ID_REV_TYPE = PARAM_ID_REV_BASE, 145 | PARAM_ID_REV_TIME, 146 | PARAM_ID_REV_DEPTH, 147 | PARAM_ID_REV_RESERVED1, 148 | PARAM_ID_REV_MIX, 149 | PARAM_ID_REV_RESERVED3, 150 | PARAM_ID_REV_LAST = PARAM_ID_REV_RESERVED3, 151 | 152 | // ARPEGGIATOR 153 | PARAM_ID_ARP_BASE, 154 | PARAM_ID_ARP_PATTERN = PARAM_ID_ARP_BASE, 155 | PARAM_ID_ARP_INTERVALS, 156 | PARAM_ID_ARP_LENGTH, 157 | PARAM_ID_ARP_STATE, 158 | PARAM_ID_ARP_TEMPO, 159 | PARAM_ID_ARP_LAST = PARAM_ID_ARP_TEMPO, 160 | 161 | NUM_PARAM_ID, // end regular param IDs 162 | 163 | // SPECIAL PARAM IDS 164 | PARAM_ID_SYS_BASE = NUM_PARAM_ID, 165 | PARAM_ID_SYS_VERSION = PARAM_ID_SYS_BASE, // read-only 166 | PARAM_ID_SYS_GLOBAL, 167 | PARAM_ID_SYS_LAST = PARAM_ID_SYS_GLOBAL, 168 | 169 | PARAM_ID_INVALID = 0x7FU, 170 | 171 | // For convenience 172 | NUM_OSC_PARAM_ID = PARAM_ID_OSC_LAST - PARAM_ID_OSC_BASE, 173 | NUM_AMPEG_PARAM_ID = PARAM_ID_AMPEG_LAST - PARAM_ID_AMPEG_BASE, 174 | NUM_FILT_PARAM_ID = PARAM_ID_FILT_LAST - PARAM_ID_FILT_BASE, 175 | NUM_MOD_PARAM_ID = PARAM_ID_MOD_LAST - PARAM_ID_MOD_BASE, 176 | NUM_DEL_PARAM_ID = PARAM_ID_DEL_LAST - PARAM_ID_DEL_BASE, 177 | NUM_REV_PARAM_ID = PARAM_ID_REV_LAST - PARAM_ID_REV_BASE, 178 | NUM_ARP_PARAM_ID = PARAM_ID_ARP_LAST - PARAM_ID_ARP_BASE, 179 | NUM_SYS_PARAM_ID = PARAM_ID_SYS_LAST - PARAM_ID_SYS_BASE, 180 | }; 181 | 182 | /** 183 | * Osc Edit Parameter Sub-IDs 184 | */ 185 | enum { 186 | // Osc edit parameters 187 | PARAM_SUBID_OSC_BASE = 0U, 188 | PARAM_SUBID_OSC_EDIT1 = PARAM_SUBID_OSC_BASE, 189 | PARAM_SUBID_OSC_EDIT2, 190 | PARAM_SUBID_OSC_EDIT3, 191 | PARAM_SUBID_OSC_EDIT4, 192 | PARAM_SUBID_OSC_EDIT5, 193 | PARAM_SUBID_OSC_EDIT6, 194 | PARAM_SUBID_OSC_LAST = PARAM_SUBID_OSC_EDIT6, 195 | NUM_OSC_PARAM_SUBID, 196 | }; 197 | 198 | /** 199 | * Global Parameter Sub-IDs 200 | */ 201 | enum { 202 | PARAM_SUBID_SYS_GLOBAL_BASE = 0, 203 | PARAM_SUBID_SYS_GLOBAL_INPUT_ROUTE = PARAM_SUBID_SYS_GLOBAL_BASE, 204 | PARAM_SUBID_SYS_GLOBAL_INPUT_TRIM, 205 | PARAM_SUBID_SYS_GLOBAL_SYNCOUT_POLARITY, 206 | PARAM_SUBID_SYS_GLOBAL_SYNCIN_POLARITY, 207 | PARAM_SUBID_SYS_GLOBAL_TEMPO_RANGE, 208 | PARAM_SUBID_SYS_GLOBAL_CLOCK_SOURCE, 209 | PARAM_SUBID_SYS_GLOBAL_SHORT_MESSAGE, 210 | PARAM_SUBID_SYS_GLOBAL_MIDI_ROUTE, 211 | PARAM_SUBID_SYS_GLOBAL_MIDI_CHANNEL, 212 | PARAM_SUBID_SYS_GLOBAL_SYNC_STEP, 213 | PARAM_SUBID_SYS_GLOBAL_LAST = PARAM_SUBID_SYS_GLOBAL_SYNC_STEP, 214 | NUM_SYS_GLOBAL_PARAM_SUBID, 215 | }; 216 | 217 | /** 218 | * Meta Sub-IDs 219 | */ 220 | enum { 221 | INVALID_PARAM_SUBID = 0xFU, 222 | }; 223 | 224 | /** 225 | * Types for edit parameters 226 | */ 227 | enum { 228 | EDIT_PARAM_TYPE_PERCENT = 0, 229 | EDIT_PARAM_TYPE_PERCENT_BIPOLAR, 230 | EDIT_PARAM_TYPE_SELECT, 231 | }; 232 | 233 | // ---------------------------------------------------------- 234 | 235 | /** 236 | * Initialize main board interface 237 | */ 238 | static inline uint8_t init() { return nts1_init(); } 239 | 240 | /** 241 | * Tear down main board interface 242 | */ 243 | static inline uint8_t teardown() { return nts1_teardown(); } 244 | 245 | /** 246 | * Process tx/rx communications with main board 247 | * Must be called regularly from the loop() function. 248 | */ 249 | static inline uint8_t idle() { return nts1_idle(); } 250 | 251 | /** 252 | * Send a parameter change message to the NTS-1 main board 253 | */ 254 | static inline uint8_t paramChange(uint8_t id, uint8_t subid, uint16_t value) { 255 | return nts1_param_change(id, subid, value); 256 | } 257 | 258 | /** 259 | * Send a note on event to the NTS-1 main board 260 | */ 261 | static inline uint8_t noteOn(uint8_t note, uint8_t velo) { 262 | return nts1_note_on(note, velo); 263 | } 264 | 265 | /** 266 | * Send a note off event to the NTS-1 main board 267 | */ 268 | static inline uint8_t noteOff(uint8_t note) { 269 | return nts1_note_off(note); 270 | } 271 | 272 | /** 273 | * Request system version from the NTS-1 main board 274 | */ 275 | static inline uint8_t reqSysVersion(void) { 276 | return nts1_req_sys_version(); 277 | } 278 | 279 | /** 280 | * Request value of given parameter from the NTS-1 main board 281 | */ 282 | static inline uint8_t reqParamValue(uint8_t id, uint8_t subid) { 283 | return nts1_req_param_value(id, subid); 284 | } 285 | 286 | /** 287 | * Request number of oscillators from the NTS-1 main board 288 | */ 289 | static inline uint8_t reqOscCount(void) { 290 | return nts1_req_osc_count(); 291 | } 292 | 293 | /** 294 | * Request oscillator descriptor from the NTS-1 main board 295 | */ 296 | static inline uint8_t reqOscDesc(uint8_t idx) { 297 | return nts1_req_osc_desc(idx); 298 | } 299 | 300 | /** 301 | * Request oscillator edit parameter descriptor from the NTS-1 main board 302 | */ 303 | static inline uint8_t reqOscEditParamDesc(uint8_t idx) { 304 | return nts1_req_osc_edit_param_desc(idx); 305 | } 306 | 307 | /** 308 | * Request number of filters from the NTS-1 main board 309 | */ 310 | static inline uint8_t reqFilterCount(void) { 311 | return nts1_req_filt_count(); 312 | } 313 | 314 | /** 315 | * Request filter descriptor from the NTS-1 main board 316 | */ 317 | static inline uint8_t reqFilterDesc(uint8_t idx) { 318 | return nts1_req_filt_desc(idx); 319 | } 320 | 321 | /** 322 | * Request number of amp. env. generators from the NTS-1 main board 323 | */ 324 | static inline uint8_t reqAmpEGCount(void) { 325 | return nts1_req_ampeg_count(); 326 | } 327 | 328 | /** 329 | * Request amp. env. generator descriptor from the NTS-1 main board 330 | */ 331 | static inline uint8_t reqAmpEGDesc(uint8_t idx) { 332 | return nts1_req_ampeg_desc(idx); 333 | } 334 | 335 | /** 336 | * Request number of modulation effects from the NTS-1 main board 337 | */ 338 | static inline uint8_t reqModCount(void) { 339 | return nts1_req_mod_count(); 340 | } 341 | 342 | /** 343 | * Request modulation effect descriptor from the NTS-1 main board 344 | */ 345 | static inline uint8_t reqModDesc(uint8_t idx) { 346 | return nts1_req_mod_desc(idx); 347 | } 348 | 349 | /** 350 | * Request number of delay effects from the NTS-1 main board 351 | */ 352 | static inline uint8_t reqDelayCount(void) { 353 | return nts1_req_del_count(); 354 | } 355 | 356 | /** 357 | * Request delay effect descriptor from the NTS-1 main board 358 | */ 359 | static inline uint8_t reqDelayDesc(uint8_t idx) { 360 | return nts1_req_del_desc(idx); 361 | } 362 | 363 | /** 364 | * Request number of reverb effects from the NTS-1 main board 365 | */ 366 | static inline uint8_t reqReverbCount(void) { 367 | return nts1_req_rev_count(); 368 | } 369 | 370 | /** 371 | * Request reverb effect descriptor from the NTS-1 main board 372 | */ 373 | static inline uint8_t reqReverbDesc(uint8_t idx) { 374 | return nts1_req_rev_desc(idx); 375 | } 376 | 377 | /** 378 | * Request number of arp. patterns from the NTS-1 main board 379 | */ 380 | static inline uint8_t reqArpPatternCount(void) { 381 | return nts1_req_arp_pattern_count(); 382 | } 383 | 384 | /** 385 | * Request arp. pattern descriptor from the NTS-1 main board 386 | */ 387 | static inline uint8_t reqArpPatternDesc(uint8_t idx) { 388 | return nts1_req_arp_pattern_desc(idx); 389 | } 390 | 391 | /** 392 | * Request number of arp. intervalss from the NTS-1 main board 393 | */ 394 | static inline uint8_t reqArpIntervalsCount(void) { 395 | return nts1_req_arp_intervals_count(); 396 | } 397 | 398 | /** 399 | * Request arp. intervals descriptor from the NTS-1 main board 400 | */ 401 | static inline uint8_t reqArpIntervalsDesc(uint8_t idx) { 402 | return nts1_req_arp_intervals_desc(idx); 403 | } 404 | 405 | /** 406 | * Register a handler function for received note off events 407 | */ 408 | void setNoteOffEventHandler(nts1_note_off_event_handler handler); 409 | 410 | /** 411 | * Register a handler function for received note on events 412 | */ 413 | void setNoteOnEventHandler(nts1_note_on_event_handler handler); 414 | 415 | /** 416 | * Register a handler function for received step tick events 417 | */ 418 | void setStepTickEventHandler(nts1_step_tick_event_handler handler); 419 | 420 | /** 421 | * Register a handler function for received unit descriptor events 422 | */ 423 | void setUnitDescEventHandler(nts1_unit_desc_event_handler handler); 424 | 425 | /** 426 | * Register a handler function for received edit parameter descriptor events 427 | */ 428 | void setEditParamDescEventHandler(nts1_edit_param_desc_event_handler handler); 429 | 430 | /** 431 | * Register a handler function for received value events 432 | */ 433 | void setValueEventHandler(nts1_value_event_handler handler); 434 | 435 | /** 436 | * Register a handler function for received parameter change messages 437 | */ 438 | void setParamChangeHandler(nts1_param_change_handler handler); 439 | 440 | }; 441 | 442 | #endif // _NTS1_H_ 443 | 444 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/variants/NTS1_REF_CP_REVC/nts1_iface.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nts1_iface.c 3 | * @brief C API handling communication with the NTS-1 digital kit's main board. 4 | * 5 | * Provides initialization of necessary peripherals, handling of low level 6 | * protocol details and convenience functions for bidrectional communication 7 | * with the NTS-1 digital kit's main board via SPI. 8 | * 9 | * BSD 3-Clause License 10 | * Copyright (c) 2020, KORG INC. 11 | * All rights reserved. 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * * Redistributions of source code must retain the above copyright notice, this 15 | * list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * * Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived from 21 | * this software without specific prior written permission. 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | //*/ 33 | 34 | #include "nts1_iface.h" 35 | 36 | #include 37 | 38 | #include "stm32f0xx_hal.h" 39 | #include "stm32f0xx_hal_def.h" 40 | #include "stm32f0xx_hal_spi.h" 41 | 42 | #include "stm32_def.h" 43 | #include "PeripheralPins.h" 44 | #include "PinAF_STM32F1.h" 45 | #include "pinconfig.h" 46 | #include "spi_com.h" 47 | 48 | #define SPI_PERIPH SPI2 49 | #define SPI_MISO_PORT GPIOB 50 | #define SPI_MISO_PIN GPIO_PIN_14 51 | #define SPI_MOSI_PORT GPIOB 52 | #define SPI_MOSI_PIN GPIO_PIN_15 53 | #define SPI_SCK_PORT GPIOB 54 | #define SPI_SCK_PIN GPIO_PIN_13 55 | #define SPI_GPIO_AF GPIO_AF0_SPI2 56 | 57 | #define SPI_IRQn SPI2_IRQn 58 | #define SPI_IRQ_PRIORITY 1 59 | #define SPI_IRQ_HANDLER SPI2_IRQHandler 60 | 61 | #define SPI_GPIO_CLK_ENA() __HAL_RCC_GPIOB_CLK_ENABLE() 62 | #define SPI_FORCE_RESET() __HAL_RCC_SPI2_FORCE_RESET() 63 | #define SPI_RELEASE_RESET() __HAL_RCC_SPI2_RELEASE_RESET() 64 | #define SPI_CLK_ENABLE() __HAL_RCC_SPI2_CLK_ENABLE() 65 | #define SPI_CLK_DISABLE() __HAL_RCC_SPI2_CLK_DISABLE() 66 | 67 | #define ACK_PORT GPIOB 68 | #define ACK_PIN GPIO_PIN_12 69 | 70 | #define ACK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() 71 | 72 | #define PANEL_ID_MASK 0x38 // Bits 3-5 // 00111000 73 | #define PANEL_CMD_EMARK 0x40 // Bit 6 // 01000000 74 | #define PANEL_START_BIT 0x80 // Bit 7 // 10000000 75 | 76 | #define SPI_TX_BUF_SIZE (0x200) 77 | #define SPI_TX_BUF_MASK (SPI_TX_BUF_SIZE - 1) 78 | 79 | #define SPI_RX_BUF_SIZE (0x200 ) 80 | #define SPI_RX_BUF_MASK (SPI_RX_BUF_SIZE - 1) 81 | 82 | #ifndef true 83 | #define true 1 84 | #endif 85 | 86 | #ifndef false 87 | #define false 0 88 | #endif 89 | 90 | // ---------------------------------------------------- 91 | 92 | enum { 93 | k_tx_cmd_event = 0x84U, 94 | k_tx_cmd_param = 0x85U, 95 | k_tx_cmd_other = 0x86U, 96 | k_tx_cmd_dummy = 0x87U 97 | }; 98 | 99 | enum { 100 | k_tx_subcmd_other_ack = 0x3U, 101 | k_tx_subcmd_other_version = 0x10U, 102 | k_tx_subcmd_other_bootmode = 0x11U, 103 | }; 104 | 105 | enum { 106 | k_rx_cmd_event = 0x84U, 107 | k_rx_cmd_param = 0x85U, 108 | k_rx_cmd_other = 0x86U, 109 | k_rx_cmd_dummy = 0x87U 110 | }; 111 | 112 | enum { 113 | k_rx_subcmd_other_panelid = 0x0U, 114 | k_rx_subcmd_other_stsreq = 0x1U, 115 | k_rx_subcmd_other_ackreq = 0x3U, 116 | }; 117 | 118 | typedef struct __nts1_cmd_header { 119 | uint8_t cmd:3; 120 | uint8_t panel_id:3; 121 | uint8_t end_mark:1; 122 | uint8_t start_bit:1; 123 | } __nts1_cmd_header_t; 124 | 125 | // ---------------------------------------------------- 126 | 127 | static SPI_HandleTypeDef s_spi; 128 | 129 | static uint8_t s_panel_id = PANEL_ID_MASK; // Bits 3-5 "ppp"="111" 130 | static uint8_t s_dummy_tx_cmd = (PANEL_ID_MASK + 0xC7); // B'11ppp111; 131 | 132 | static uint8_t s_started; 133 | 134 | static uint8_t s_spi_tx_buf[SPI_TX_BUF_SIZE]; 135 | static uint16_t s_spi_tx_ridx; // Read Index (from s_spi_tx_buf) 136 | static uint16_t s_spi_tx_widx; // Write Index (to s_spi_tx_buf) 137 | 138 | static uint8_t s_spi_rx_buf[SPI_RX_BUF_SIZE]; 139 | static uint16_t s_spi_rx_ridx; // Read Index (from s_spi_rx_buf) 140 | static uint16_t s_spi_rx_widx; // Write Index (to s_spi_rx_buf) 141 | 142 | static uint8_t s_panel_rx_status; 143 | static uint8_t s_panel_rx_data_cnt; 144 | static uint8_t s_panel_rx_data[127]; 145 | 146 | // ---------------------------------------------------- 147 | 148 | #define SPI_TX_BUF_RESET() (s_spi_tx_ridx = s_spi_tx_widx = 0) 149 | #define SPI_TX_BUF_EMPTY() ((SPI_TX_BUF_MASK & s_spi_tx_ridx) == (SPI_TX_BUF_MASK & s_spi_tx_widx)) 150 | #define SPI_RX_BUF_RESET() (s_spi_rx_ridx = s_spi_rx_widx = 0) 151 | #define SPI_RX_BUF_EMPTY() ((SPI_RX_BUF_MASK & s_spi_rx_ridx) == (SPI_RX_BUF_MASK & s_spi_rx_widx)) 152 | 153 | #define SPI_BUF_INC(idx, bufSize) (((idx+1) == bufSize) ? 0 : idx + 1) 154 | 155 | // ---------------------------------------------------- 156 | 157 | static inline void s_port_startup_ack(void) 158 | { 159 | // This sets the ACK_PIN GPIO pin to 1 160 | // ACK_PORT is the GPIO device (of which the STM32 has 2) 161 | // BSRR is the Bit Set Reset Register. 162 | // Setting one or more bits in this register will write to the 163 | // corresponding GPIO pin. 164 | // This is an atomic operation (takes 1 system clock tick), 165 | // as oppossed to setting 1 bit in general GPIO register, 166 | // which would force the CPU to first read the GPIO register, 167 | // perform the bitwise operation, and then set the GPIO register 168 | // again. 169 | ACK_PORT->BSRR = ACK_PIN; 170 | // NOTE: C doesn't guarantee that operations will be atomic. 171 | } 172 | 173 | static inline void s_port_wait_ack(void) 174 | { 175 | // This sets the ACK_PIN GPIO pin to 0 176 | // Same as above, but the register is the Bit Reset Register 177 | ACK_PORT->BRR = ACK_PIN; 178 | } 179 | 180 | // ---------------------------------------------------- 181 | static inline void s_spi_raw_fifo_push8(SPI_TypeDef* SPIx, uint8_t data) 182 | { 183 | // SPIx is a pointer, so will a contain a memory address of the SPI device 184 | // 0x0C is the offset of the Data Register with respect to the SPI device. 185 | // The casting "(uint32_t)" is necessary here because SPIx is a pointer address, 186 | // but it's trying to save it into a non-pointer variable. The compiler won't allow 187 | // this unless you force it by doing the casting. 188 | const uint32_t spix_dr = (uint32_t)SPIx + 0x0C; 189 | // spix_dr is the address of the SPIX_DR data register 190 | // Reading from this address will return the oldest frame of data from the Rx FIFO 191 | // Writing to this address will write to the end of the Tx FIFO 192 | 193 | // In this case, we are writing to it 194 | *(__IO uint8_t *) spix_dr = data; 195 | // *(__IO uint8_t *) spix_dr = data means: 196 | // - (__IO uint8_t *) spix_dr: cast spidx_dr to a pointer (*) of type __IO uint8_t 197 | // - first "*": derreference the pointer we just casted and 198 | // - "= data": assign the value of data to the derefferenced location 199 | // In other words, it's equivalent to: 200 | // __IO uint8_t* ptr_spix_dr = (__IO uint8_t *) spix_dr; // again, casting to change from uint to *uint 201 | // *ptr_spix_dr = data; 202 | } 203 | 204 | static uint8_t s_spi_chk_rx_buf_space(uint16_t size) 205 | { 206 | uint16_t count; 207 | 208 | if (s_spi_rx_ridx <= s_spi_rx_widx) { 209 | count = (SPI_RX_BUF_SIZE + s_spi_rx_ridx) - s_spi_rx_widx; 210 | } else { 211 | count = s_spi_rx_ridx - s_spi_rx_widx; 212 | } 213 | return (count > size); 214 | } 215 | 216 | static uint8_t s_spi_rx_buf_write(uint8_t data) 217 | { 218 | uint16_t bufdatacount; 219 | if (s_spi_rx_ridx <= s_spi_rx_widx) { 220 | bufdatacount = s_spi_rx_widx - s_spi_rx_ridx; 221 | } else { 222 | bufdatacount = SPI_RX_BUF_SIZE + s_spi_rx_widx - s_spi_rx_ridx; 223 | } 224 | if (bufdatacount < (SPI_RX_BUF_SIZE - 2)) { 225 | s_spi_rx_buf[SPI_RX_BUF_MASK & s_spi_rx_widx] = data; 226 | s_spi_rx_widx = SPI_BUF_INC(s_spi_rx_widx, SPI_RX_BUF_SIZE); 227 | return true; 228 | } 229 | return false; 230 | } 231 | 232 | static uint8_t s_spi_rx_buf_read(void) 233 | { 234 | const uint8_t data = s_spi_rx_buf[SPI_RX_BUF_MASK & s_spi_rx_ridx]; 235 | s_spi_rx_ridx = SPI_BUF_INC(s_spi_rx_ridx, SPI_RX_BUF_SIZE); 236 | return data; 237 | } 238 | 239 | static uint8_t s_spi_chk_tx_buf_space(uint16_t size) 240 | { 241 | uint16_t count; 242 | if (s_spi_tx_ridx <= s_spi_tx_widx) { 243 | count = SPI_TX_BUF_SIZE + s_spi_tx_ridx - s_spi_tx_widx; 244 | } else { 245 | count = s_spi_tx_ridx - s_spi_tx_widx; 246 | } 247 | return (count > size); 248 | } 249 | 250 | // writes the byte to the buffer at the appropiate position 251 | // and moves the buffer pointers 252 | static void s_spi_tx_buf_write(uint8_t data) 253 | { 254 | s_spi_tx_buf[SPI_TX_BUF_MASK & s_spi_tx_widx] = data; 255 | s_spi_tx_widx = SPI_BUF_INC(s_spi_tx_widx, SPI_TX_BUF_SIZE); 256 | } 257 | 258 | static uint8_t s_spi_tx_buf_read(void) 259 | { 260 | const uint8_t data = s_spi_tx_buf[SPI_TX_BUF_MASK & s_spi_tx_ridx]; 261 | s_spi_tx_ridx = SPI_BUF_INC(s_spi_tx_ridx, SPI_TX_BUF_SIZE); 262 | return data; 263 | } 264 | 265 | // ---------------------------------------------------- 266 | 267 | static inline void s_spi_struct_init(SPI_InitTypeDef* SPI_InitStruct) 268 | { 269 | SPI_InitStruct->Mode = SPI_MODE_SLAVE; 270 | SPI_InitStruct->Direction = SPI_DIRECTION_2LINES; 271 | SPI_InitStruct->DataSize = SPI_DATASIZE_8BIT; 272 | SPI_InitStruct->CLKPolarity = SPI_POLARITY_HIGH; 273 | SPI_InitStruct->CLKPhase = SPI_PHASE_2EDGE; 274 | SPI_InitStruct->NSS = SPI_NSS_SOFT; 275 | SPI_InitStruct->BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; 276 | SPI_InitStruct->FirstBit = SPI_FIRSTBIT_LSB; 277 | SPI_InitStruct->TIMode = SPI_TIMODE_DISABLE; 278 | SPI_InitStruct->CRCCalculation = SPI_CRCCALCULATION_DISABLE; 279 | SPI_InitStruct->CRCPolynomial = 7; 280 | SPI_InitStruct->CRCLength = SPI_CRC_LENGTH_DATASIZE; 281 | SPI_InitStruct->NSSPMode = SPI_NSS_PULSE_DISABLE; 282 | } 283 | 284 | static inline void s_spi_enable_pins() 285 | { 286 | // Same as ACK_GPIO_CLK_ENABLE, 287 | // mapped to __HAL_RCC_GPIOB_CLK_ENABLE 288 | SPI_GPIO_CLK_ENA(); 289 | 290 | GPIO_InitTypeDef gpio; 291 | 292 | /* Enable SCK, MOSI, MISO. No NSS. */ 293 | /* Peripherals alternate function */ 294 | gpio.Mode = GPIO_MODE_AF_PP; 295 | gpio.Speed = GPIO_SPEED_FREQ_HIGH; 296 | gpio.Pull = GPIO_NOPULL; 297 | // GPIO pins set to alternate function, which allows alternating the pins 298 | // between GPIO functions and internal peripheral functions 299 | // This is a requirement of the SPI device in the STM32 300 | // See Reference Manual, Figure 274 301 | gpio.Alternate = SPI_GPIO_AF; 302 | 303 | gpio.Pin = SPI_MISO_PIN; 304 | HAL_GPIO_Init(SPI_MISO_PORT, &gpio); 305 | 306 | gpio.Pin = SPI_MOSI_PIN; 307 | HAL_GPIO_Init(SPI_MOSI_PORT, &gpio); 308 | 309 | gpio.Pin = SPI_SCK_PIN; 310 | gpio.Pull = GPIO_PULLUP; 311 | HAL_GPIO_Init(SPI_SCK_PORT, &gpio); 312 | 313 | } 314 | 315 | HAL_StatusTypeDef s_spi_init() 316 | { 317 | // Enables the clock for the SYSCFG register 318 | // which is used for specific configurations of memory and 319 | // DMA requests remap and to control special I/O features. 320 | __HAL_RCC_SYSCFG_CLK_ENABLE(); 321 | 322 | // Configures the GPIO pins for SPI 323 | // See below 324 | s_spi_enable_pins(); 325 | 326 | // Resets and enables clock signals for SPI 327 | SPI_FORCE_RESET(); 328 | SPI_RELEASE_RESET(); 329 | SPI_CLK_ENABLE(); 330 | 331 | // Configures SPI as seen above 332 | // Note that SPI_PERIPH is mapped to SPI2, which is one 333 | // of the 2 available SPI devices in the MCU 334 | s_spi.Instance = SPI_PERIPH; 335 | s_spi_struct_init(&(s_spi.Init)); 336 | 337 | // Initializes the SPI driver 338 | const HAL_StatusTypeDef res = HAL_SPI_Init(&s_spi); 339 | if (res != HAL_OK) { 340 | return res; 341 | } 342 | 343 | // Sets the interrupt handler in the NVIC (Nested Vector Interrupt Controller) 344 | HAL_NVIC_SetPriority(SPI_IRQn, SPI_IRQ_PRIORITY, 0); 345 | HAL_NVIC_EnableIRQ(SPI_IRQn); 346 | 347 | // Adds (bitwise OR) RXNE to the SPI control register 2 348 | // This enables the "RX buffer not empty" interrupt 349 | // See Reference Manual 28.5.10 350 | SPI_PERIPH->CR2 |= SPI_IT_RXNE; 351 | 352 | // Empties the buffers for transmission and reception 353 | // and reset counters 354 | s_panel_rx_status = 0; 355 | s_panel_rx_data_cnt = 0; 356 | SPI_RX_BUF_RESET(); 357 | SPI_TX_BUF_RESET(); 358 | 359 | // Finally enables HAL SPI 360 | __HAL_SPI_ENABLE(&s_spi); 361 | 362 | return HAL_OK; 363 | } 364 | 365 | 366 | HAL_StatusTypeDef s_spi_teardown() 367 | { 368 | __HAL_SPI_DISABLE(&s_spi); 369 | SPI_CLK_DISABLE(); 370 | return HAL_OK; 371 | } 372 | 373 | // ---------------------------------------------------- 374 | 375 | static uint8_t s_tx_cmd_event(const nts1_tx_event_t *event, uint8_t endmark) 376 | { 377 | assert(event != NULL); 378 | if (!s_spi_chk_tx_buf_space(4)) 379 | return false; 380 | // if (s_panel_id & PANEL_ID_MASK) + (endmark) > 0, add PANEL_CMD_EMARK bit to the 381 | // command byte 382 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_event | PANEL_CMD_EMARK) : k_tx_cmd_event; 383 | s_spi_tx_buf_write(cmd); 384 | s_spi_tx_buf_write(event->event_id & 0x7F); 385 | s_spi_tx_buf_write(event->msb & 0x7F); 386 | s_spi_tx_buf_write(event->lsb & 0x7F); 387 | return true; 388 | } 389 | 390 | static uint8_t s_tx_cmd_param_change(const nts1_tx_param_change_t *param_change, uint8_t endmark) 391 | { 392 | assert(param_change != NULL); 393 | if (!s_spi_chk_tx_buf_space(4)) 394 | return false; 395 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_param | PANEL_CMD_EMARK) : k_tx_cmd_param; 396 | s_spi_tx_buf_write(cmd); 397 | s_spi_tx_buf_write(param_change->param_id & 0x7F); 398 | s_spi_tx_buf_write(param_change->param_subid & 0x7F); 399 | s_spi_tx_buf_write(param_change->msb & 0x7F); 400 | s_spi_tx_buf_write(param_change->lsb & 0x7F); 401 | return true; 402 | } 403 | 404 | static uint8_t s_tx_cmd_other_ack(uint8_t endmark) 405 | { 406 | if (!s_spi_chk_tx_buf_space(3)) 407 | return false; 408 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_other | PANEL_CMD_EMARK) : k_tx_cmd_other; 409 | s_spi_tx_buf_write(cmd); 410 | s_spi_tx_buf_write(3); 411 | s_spi_tx_buf_write(k_tx_subcmd_other_ack); 412 | return true; 413 | } 414 | 415 | static uint8_t s_tx_cmd_other_version(uint8_t endmark) 416 | { 417 | if (!s_spi_chk_tx_buf_space(5)) 418 | return false; 419 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_other | PANEL_CMD_EMARK) : k_tx_cmd_other; 420 | s_spi_tx_buf_write(cmd); 421 | s_spi_tx_buf_write(5); 422 | s_spi_tx_buf_write(k_tx_subcmd_other_version); 423 | s_spi_tx_buf_write(1); 424 | s_spi_tx_buf_write(0); 425 | return true; 426 | } 427 | 428 | static uint8_t s_tx_cmd_other_bootmode(uint8_t endmark) 429 | { 430 | if (!s_spi_chk_tx_buf_space(4)) 431 | return false; 432 | const uint8_t cmd = (s_panel_id & PANEL_ID_MASK) + (endmark) ? (k_tx_cmd_other | PANEL_CMD_EMARK) : k_tx_cmd_other; 433 | s_spi_tx_buf_write(cmd); 434 | s_spi_tx_buf_write(4); 435 | s_spi_tx_buf_write(k_tx_subcmd_other_bootmode); 436 | s_spi_tx_buf_write(0); 437 | return true; 438 | } 439 | 440 | // ---------------------------------------------------- 441 | 442 | static uint8_t s_dummy_buffer[64]; 443 | #define RX_EVENT_MAX_DECODE_SIZE 64 444 | static uint8_t s_rx_event_decode_buf[RX_EVENT_MAX_DECODE_SIZE] = {0}; 445 | 446 | // This is the handler itself 447 | static void s_rx_msg_handler(uint8_t data) 448 | { 449 | // data is 1 byte of the input buffer 450 | // If data byte starts with 10000000 451 | if (data >= PANEL_START_BIT) { 452 | // Status byte 453 | // resets the counter 454 | s_panel_rx_data_cnt = 0; 455 | // data = data AND not(PANEL_CMD_EMARK) 456 | // PANEL_CMD_EMARK is 01000000 457 | // not(PANEL_CMD_EMARK) is 10111111 458 | // so, the following discards the bit at position 6 (starting from pos 0) 459 | data &= ~PANEL_CMD_EMARK; 460 | 461 | if (data == 0xBEU) { // 10111110:Panel ID allocation 462 | // if data == 1x111110 463 | s_panel_rx_status = data & ~PANEL_ID_MASK; // discards bits 3,4 and 5 464 | //s_panel_rx_status = 10111110 * 11000111 => 10000110 465 | } else if ( 466 | (data & PANEL_ID_MASK) // bits 3 4 5 of data (00xxx000) 467 | == 468 | (s_panel_id & PANEL_ID_MASK) // 00111000 469 | ) { // if data contains (xx111xxx) 470 | s_panel_rx_status = data & ~PANEL_ID_MASK; // produces 10000xxx 471 | } else { 472 | s_panel_rx_status = 0; // cancel any previous command reception 473 | } 474 | // exits the method to parse next byte 475 | return; 476 | } 477 | // the previous section stores in s_panel_rx_status the first byte 478 | // received, identified by having its first bit set to 1 (1xxxxxxx) 479 | // and it processes it to discard irrelevant data 480 | 481 | // Relevant statuses: 482 | // if data == 1x111110 => active_cmd = 10000110 483 | // if data == 1x111xxx => active_cmd = 10000xxx 484 | // otherwise, status byte is discarded 485 | 486 | // Stored status byte 487 | const uint8_t active_cmd = s_panel_rx_status; 488 | 489 | // Now we process bytes that don't start with 1 490 | switch (active_cmd) { 491 | // 1x111 100 492 | case k_rx_cmd_event: 493 | { 494 | // this will push stuff into the array 495 | // for future use 496 | s_panel_rx_data[s_panel_rx_data_cnt++] = data; 497 | 498 | // transactions are at least 3 bytes long 499 | if (s_panel_rx_data_cnt < 2) 500 | break; // need more data 501 | 502 | // The second byte always contains the length of the message 503 | // so this will wait until all the bytes have been received to process the message 504 | // It also subtracts 1 from the indicated size, because the first byte 505 | // (the status byte), is not stored in this array 506 | if (s_panel_rx_data_cnt < (s_panel_rx_data[0] - 1)) 507 | break; // need more data 508 | 509 | /*++++++++++++++++++++++++++++++++++++++++++++++ 510 | CMD4 : Event 511 | 1st :[1][0][ppp][100] 512 | 2nd :[0][sssssss] Size 513 | 3rd :[0][eeeeeee] Event ID 514 | 4th :[0][ddddddd] Data word 515 | ... 516 | +++++++++++++++++++++++++++++++++++++++++++++*/ 517 | 518 | 519 | const nts1_rx_event_header_t *rx_event = (const nts1_rx_event_header_t *)s_panel_rx_data; 520 | // payload is pointer to the position of s_panel_rx_data + nts1_rx_event_header_t 521 | // which I guess is the memory position of the actual data 522 | const uint8_t *payload = s_panel_rx_data + sizeof(nts1_rx_event_header_t); 523 | 524 | const uint32_t payload_size7 = (rx_event->size - sizeof(nts1_rx_event_header_t) - 1); 525 | const uint32_t payload_size8 = nts1_size_7to8(payload_size7); 526 | if (payload_size8 > RX_EVENT_MAX_DECODE_SIZE) { 527 | // Reset rx status 528 | s_panel_rx_status = 0; 529 | s_panel_rx_data_cnt = 0; 530 | break; 531 | } 532 | nts1_convert_7to8(s_rx_event_decode_buf, payload, payload_size7); 533 | 534 | switch (rx_event->event_id) { 535 | case k_nts1_rx_event_id_note_off: 536 | if (payload_size8 == sizeof(nts1_rx_note_off_t)) 537 | nts1_handle_note_off_event((const nts1_rx_note_off_t *)s_rx_event_decode_buf); 538 | break; 539 | case k_nts1_rx_event_id_note_on: 540 | if (payload_size8 == sizeof(nts1_rx_note_on_t)) 541 | nts1_handle_note_on_event((const nts1_rx_note_on_t *)s_rx_event_decode_buf); 542 | break; 543 | case k_nts1_rx_event_id_step_tick: 544 | nts1_handle_step_tick_event(); 545 | break; 546 | case k_nts1_rx_event_id_unit_desc: 547 | //if (payload_size8 == sizeof(nts1_rx_unit_desc_t)) 548 | nts1_handle_unit_desc_event((const nts1_rx_unit_desc_t *)s_rx_event_decode_buf); 549 | break; 550 | case k_nts1_rx_event_id_edit_param_desc: 551 | if (payload_size8 == sizeof(nts1_rx_edit_param_desc_t)) { 552 | nts1_handle_edit_param_desc_event((const nts1_rx_edit_param_desc_t *)s_rx_event_decode_buf); 553 | } 554 | break; 555 | case k_nts1_rx_event_id_value: 556 | if (payload_size8 == sizeof(nts1_rx_value_t)) 557 | nts1_handle_value_event((const nts1_rx_value_t *)s_rx_event_decode_buf); 558 | break; 559 | default: 560 | break; 561 | } 562 | 563 | // Reset rx status 564 | s_panel_rx_status = 0; 565 | s_panel_rx_data_cnt = 0; 566 | } 567 | break; 568 | 569 | // 1x111 101 570 | case k_rx_cmd_param: 571 | { 572 | s_panel_rx_data[s_panel_rx_data_cnt++] = data; 573 | 574 | if (s_panel_rx_data_cnt < 4) 575 | break; // need more data 576 | 577 | /*++++++++++++++++++++++++++++++++++++++++++++++ 578 | CMD5 : Param Change 579 | 1st :[1][0][ppp][101] 580 | 2nd :[0][eeeeeee] Param ID 581 | 3rd :[0][sssssss] Param Sub ID 582 | 4th :[0][hhhhhhh] MSB 583 | 5th :[0][lllllll] LSB 584 | +++++++++++++++++++++++++++++++++++++++++++++*/ 585 | 586 | const nts1_rx_param_change_t *rx_param = (const nts1_rx_param_change_t *)s_panel_rx_data; 587 | nts1_handle_param_change(rx_param); 588 | 589 | // Reset rx status 590 | s_panel_rx_status = 0; 591 | s_panel_rx_data_cnt = 0; 592 | } 593 | break; 594 | 595 | // 1x111 110 596 | case k_rx_cmd_other: 597 | { 598 | // continue reading until we have the full command 599 | s_panel_rx_data[s_panel_rx_data_cnt++] = data; 600 | // it's important to note that here, if s_panel_rx_data[0] (size) 601 | // is 0, the break will never happen 602 | if (s_panel_rx_data_cnt < (s_panel_rx_data[0] - 1)) 603 | break; // need more data 604 | 605 | if (s_panel_rx_data_cnt < 2) { 606 | // Command too short - ignore and reset 607 | s_panel_rx_status = 0; 608 | s_panel_rx_data_cnt = 0; 609 | break; 610 | } 611 | 612 | // switches on the 3rd byte (MessageID) 613 | switch (s_panel_rx_data[1]) { 614 | case k_rx_subcmd_other_panelid: // Panel ID specified ("ppp" is left here but not here) 615 | /*++++++++++++++++++++++++++++++++++++++++++++++ 616 | CMD6-0 :PanelID designation. 617 | Only in this case, specify ppp=7. 618 | 1st :[1][0][111][110] ppp=7 use 619 | 2nd :[0][0000100] Size=4 620 | 3rd :[0][0000000] MessageID = 0 621 | 4th :[0][0000PPP] The PanelID number you specify. 622 | +++++++++++++++++++++++++++++++++++++++++++++*/ 623 | if (s_panel_rx_data_cnt >= 3 && s_panel_rx_data[0] == 4) { 624 | s_panel_id = ((s_panel_rx_data[2] & 0x07) << 3) & PANEL_ID_MASK; 625 | s_dummy_tx_cmd = s_panel_id | 0xC7; // B'11ppp111; 626 | // Send Version to HOST 627 | s_tx_cmd_other_version(false); 628 | // Send All SW Pattern to HOST 629 | s_tx_cmd_other_bootmode(true); 630 | } 631 | // Reset rx status 632 | s_panel_rx_status = 0; 633 | s_panel_rx_data_cnt = 0; 634 | break; 635 | 636 | case k_rx_subcmd_other_stsreq: 637 | /*++++++++++++++++++++++++++++++++++++++++++++++ 638 | CMD6-1 :Status Request 639 | Request to send the current SwitchPattern (CMD6-17) and all knob commands. 640 | 1st :[1][0][ppp][110] 641 | 2nd :[0][0000011] Size=3 642 | 3rd :[0][0000001] MessageID = 1 643 | +++++++++++++++++++++++++++++++++++++++++++++*/ 644 | s_tx_cmd_other_bootmode(true); 645 | // Reset rx status 646 | s_panel_rx_status = 0; 647 | s_panel_rx_data_cnt = 0; 648 | break; 649 | 650 | case k_rx_subcmd_other_ackreq: // Panel ACK req 651 | /*++++++++++++++++++++++++++++++++++++++++++++++ 652 | CMD6-3 :ACK request 653 | For checking whether the Panel is operating normally. 654 | When Panel receives this, it returns an ACK command. 655 | 1st :[1][0][ppp][110] 656 | 2nd :[0][0000011] Size=3 657 | 3rd :[0][0000011] MessageID = 3 658 | +++++++++++++++++++++++++++++++++++++++++++++*/ 659 | s_tx_cmd_other_ack(true); 660 | // Reset rx status 661 | s_panel_rx_status = 0; 662 | s_panel_rx_data_cnt = 0; 663 | break; 664 | 665 | default: 666 | // Undefined command - ignore and reset 667 | s_panel_rx_status = 0; 668 | s_panel_rx_data_cnt = 0; 669 | break; 670 | } 671 | } // end case k_rx_cmd_other: 672 | // 1x111 111 673 | case k_rx_cmd_dummy: 674 | // continues to default 675 | default: 676 | // resets 677 | s_panel_rx_status = 0; // Clear save status 678 | s_panel_rx_data_cnt = 0; // Initialize data count 679 | break; 680 | } 681 | } 682 | 683 | // ---------------------------------------------------- 684 | 685 | extern void SPI_IRQ_HANDLER() 686 | { 687 | volatile uint16_t sr; 688 | uint8_t txdata, rxdata; 689 | 690 | // HOST -> PANEL 691 | // While the RX FIFO is not empty 692 | // This is signified by the RXNE flag in the SPI Status Registry being 1 693 | // and calculated from the SPI Status registry 694 | // by doing a bitwise AND between the SPI_SR_RXNE flag and the SR itself 695 | // see Reference Manual 28.9.3 696 | while ((sr = SPI_PERIPH->SR) & SPI_SR_RXNE) { 697 | // Take out 8 bits from the FIFO 698 | rxdata = s_spi_raw_fifo_pop8(SPI_PERIPH); // DR read clears RXNE flag 699 | // And write it into the software RX buffer 700 | // This function only writes if the buffer has enough space to accomodate the byte 701 | if (!s_spi_rx_buf_write(rxdata)) { // write to the RX buffer 702 | // If there's not enough space in the buffer 703 | // this resets the index read and write indexes for the buffer 704 | // which will cause it to start writing into the buffer from the beginning 705 | 706 | // If RxBuf is full, reset it. 707 | SPI_RX_BUF_RESET(); 708 | } 709 | else { 710 | // If there is not 32 bits of space 711 | if (!s_spi_chk_rx_buf_space(32)) { 712 | // the ACK pin is set to 0 713 | s_port_wait_ack(); 714 | // I assume this means the NTS1 will stop sending data 715 | } else { //Remaining buffer 716 | // otherwise the ACK pin is set to 1 717 | s_port_startup_ack(); 718 | // which will allow the NTS1 to send data 719 | } 720 | } 721 | } 722 | // so, basically, as long as there is data in the RX FIFO 723 | // read it and put it in the RX buffer 724 | // if there's no space, the RX write and read position pointers 725 | // are reset, so that we can start writing from the beginning of the 726 | // buffer again. 727 | // Do this until there's no more data in the RC FIFO (the RXNE flag clears then) 728 | 729 | // HOST <- PANEL 730 | if (!SPI_TX_BUF_EMPTY()) { // Send buffer has data 731 | // If there's data to be sent 732 | txdata = s_spi_tx_buf_read(); // read it and advance the read idx pointer 733 | if (txdata & PANEL_START_BIT) { // In Status, check whether EndMark is added. 734 | // and the data contains 0x80 (is a Status message) 735 | if (!SPI_TX_BUF_EMPTY()) { // There is (more) data (after the status) to be sent next in the send buffer 736 | // an EMARK is set on the status 737 | txdata |= PANEL_CMD_EMARK; 738 | // Note: this will set endmark on almost any status, especially those who have pending data, 739 | // which seems to contradict the endmark common usage of marking only the last command of a group 740 | } 741 | } 742 | // Data is sent to the Tx FIFO register 743 | s_spi_raw_fifo_push8(SPI_PERIPH, txdata); 744 | } 745 | else { // Set the dummy because the send buffer is empty. 746 | // Dummy data is sent to the TX FIFO register 747 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 748 | } 749 | 750 | // write one byte of data from the Tx buffer to the Tx FIFO. 751 | // if the byte is a Status message, and it has more data after it, 752 | // add the ENDMARK (byte 7 set to 1) 753 | // If there's no data to be sent, write the DUMMY message 754 | } 755 | // ---------------------------------------------------- 756 | 757 | nts1_status_t nts1_init() 758 | { 759 | // Enables sampling the GPIO pin at clock rate 760 | // This is mapped to __HAL_RCC_GPIOB_CLK_ENABLE 761 | ACK_GPIO_CLK_ENABLE(); 762 | 763 | GPIO_InitTypeDef gpio; 764 | 765 | // Configures a GPIO pin for ACK (acknowledge message) 766 | /* PANEL ACK */ 767 | gpio.Mode = GPIO_MODE_OUTPUT_PP; // normal output, not Open Collector or Open Drain 768 | gpio.Speed = GPIO_SPEED_FREQ_HIGH; 769 | gpio.Pull = GPIO_NOPULL; 770 | gpio.Alternate = 0; 771 | gpio.Pin = ACK_PIN; 772 | HAL_GPIO_Init(ACK_PORT, &gpio); 773 | 774 | // More on this below 775 | HAL_StatusTypeDef res = s_spi_init(); 776 | if (res != HAL_OK) 777 | return (nts1_status_t)res; 778 | 779 | // Fills the hardware transmission FIFO register with dummy data 780 | // Fill TX FIFO 781 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 782 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 783 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 784 | s_spi_raw_fifo_push8(SPI_PERIPH, s_dummy_tx_cmd); 785 | //*/ 786 | 787 | // Sets the ACK pin to 1 788 | // More below 789 | s_port_startup_ack(); 790 | s_started = true; 791 | 792 | return k_nts1_status_ok; 793 | } 794 | 795 | nts1_status_t nts1_teardown() 796 | { 797 | HAL_StatusTypeDef res = s_spi_teardown(); 798 | 799 | return (nts1_status_t)res; 800 | } 801 | 802 | nts1_status_t nts1_idle() 803 | { 804 | // Return of HOST communication Check 805 | // This should be true right after executing setup() 806 | if (s_started) { 807 | // Checks if the reception buffer is not full 808 | if (s_spi_chk_rx_buf_space(32)) { 809 | // Sets the ACK pin to 1 810 | s_port_startup_ack(); 811 | } 812 | } 813 | 814 | // HOST I/F Give priority to Idle processing of received data 815 | // As long as the reception buffer is not empty 816 | while (!SPI_RX_BUF_EMPTY()) { 817 | // Reads from the buffer and executes the handler 818 | // Data in receive buffer 819 | s_rx_msg_handler(s_spi_rx_buf_read()); 820 | } 821 | } 822 | 823 | // ---------------------------------------------------- 824 | 825 | nts1_status_t nts1_send_events(nts1_tx_event_t *events, uint8_t count) 826 | { 827 | assert(events != NULL); 828 | for (uint8_t i=0; i < count; ++i) { 829 | if (!s_tx_cmd_event(&events[i], (i == count-1))) { 830 | return k_nts1_status_busy; 831 | } 832 | } 833 | return k_nts1_status_ok; 834 | } 835 | 836 | nts1_status_t nts1_send_param_changes(nts1_tx_param_change_t *param_changes, uint8_t count) 837 | { 838 | assert(param_changes != NULL); 839 | for (uint8_t i=0; i < count; ++i) { 840 | if (!s_tx_cmd_param_change(¶m_changes[i], (i == count-1))) { 841 | return k_nts1_status_busy; 842 | } 843 | } 844 | return k_nts1_status_ok; 845 | } 846 | 847 | uint32_t nts1_convert_7to8(uint8_t *dest8, const uint8_t *src7, uint32_t size7) { 848 | const uint32_t size8 = nts1_size_7to8(size7); 849 | const uint8_t *src7_e = src7 + size7; 850 | for (uint32_t i7 = 0, i8 = 0; i7 < size7; ++i7) { 851 | const uint8_t i7mod8 = i7 % 8; 852 | switch (i7mod8) { 853 | case 0: 854 | dest8[i8++] = src7[i7] & 0x7F; 855 | break; 856 | case 7: 857 | dest8[i8-1] |= (src7[i7] & 0x7F)<<1; 858 | break; 859 | default: 860 | { 861 | const uint8_t offset = 8-i7mod8; 862 | const uint8_t src = src7[i7]; 863 | dest8[i8-1] |= (src & ((1U<>i7mod8; 865 | } 866 | break; 867 | } 868 | } 869 | return size8; 870 | } 871 | 872 | uint32_t nts1_convert_8to7(uint8_t *dest7, const uint8_t *src8, uint32_t size8) { 873 | const uint32_t size7 = nts1_size_8to7(size8); 874 | const uint8_t * dest7_e = dest7 + size7; 875 | for (uint32_t i7 = 0, i8 = 0; i7 < size7; ++i7) { 876 | const uint8_t i7mod8 = i7 % 8; 877 | switch (i7mod8) { 878 | case 0: 879 | dest7[i7] = src8[i8++] & 0x7F; 880 | break; 881 | case 7: 882 | dest7[i7] = (src8[i8-1] & (0x7F<<1))>>1; 883 | break; 884 | default: 885 | { 886 | const uint8_t offset = 8-i7mod8; 887 | uint8_t dest = (src8[i8-1] & (0xFFU<>offset; 888 | dest |= (src8[i8++] & (0x7F>>i7mod8))<> 7) & 0x7F; 902 | param.lsb = value & 0x7F; 903 | return nts1_send_param_change(¶m); 904 | } 905 | 906 | nts1_status_t nts1_note_on(uint8_t note, uint8_t velo) { 907 | nts1_tx_event_t event; 908 | event.event_id = k_nts1_tx_event_id_note_on; 909 | event.msb = note & 0x7F; 910 | event.lsb = velo & 0x7F; 911 | return nts1_send_event(&event); 912 | } 913 | 914 | nts1_status_t nts1_note_off(uint8_t note) { 915 | nts1_tx_event_t event; 916 | event.event_id = k_nts1_tx_event_id_note_off; 917 | event.msb = note & 0x7F; 918 | event.lsb = 0x00; 919 | return nts1_send_event(&event); 920 | } 921 | 922 | nts1_status_t nts1_req_sys_version(void) { 923 | nts1_tx_event_t event; 924 | event.event_id = k_nts1_tx_event_id_req_value; 925 | event.msb = k_param_id_sys_version; 926 | event.lsb = 0x0; 927 | return nts1_send_event(&event); 928 | } 929 | 930 | nts1_status_t nts1_req_param_value(uint8_t id, uint8_t subid) { 931 | nts1_tx_event_t event; 932 | event.event_id = k_nts1_tx_event_id_req_value; 933 | event.msb = id; 934 | event.lsb = subid; 935 | return nts1_send_event(&event); 936 | } 937 | 938 | nts1_status_t nts1_req_osc_count(void) { 939 | nts1_tx_event_t event; 940 | event.event_id = k_nts1_tx_event_id_req_unit_count; 941 | event.msb = k_param_id_osc_type; 942 | event.lsb = 0x0; 943 | return nts1_send_event(&event); 944 | } 945 | 946 | nts1_status_t nts1_req_osc_desc(uint8_t idx) { 947 | nts1_tx_event_t event; 948 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 949 | event.msb = k_param_id_osc_type; 950 | event.lsb = idx & 0x7F; 951 | return nts1_send_event(&event); 952 | } 953 | 954 | nts1_status_t nts1_req_osc_edit_param_desc(uint8_t idx) { 955 | nts1_tx_event_t event; 956 | event.event_id = k_nts1_tx_event_id_req_edit_param_desc; 957 | event.msb = k_param_id_osc_type; 958 | event.lsb = idx; 959 | return nts1_send_event(&event); 960 | } 961 | 962 | nts1_status_t nts1_req_filt_count(void) { 963 | nts1_tx_event_t event; 964 | event.event_id = k_nts1_tx_event_id_req_unit_count; 965 | event.msb = k_param_id_filt_type; 966 | event.lsb = 0x0; 967 | return nts1_send_event(&event); 968 | } 969 | 970 | nts1_status_t nts1_req_filt_desc(uint8_t idx) { 971 | nts1_tx_event_t event; 972 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 973 | event.msb = k_param_id_filt_type; 974 | event.lsb = idx & 0x7F; 975 | return nts1_send_event(&event); 976 | } 977 | 978 | nts1_status_t nts1_req_ampeg_count(void) { 979 | nts1_tx_event_t event; 980 | event.event_id = k_nts1_tx_event_id_req_unit_count; 981 | event.msb = k_param_id_ampeg_type; 982 | event.lsb = 0x0; 983 | return nts1_send_event(&event); 984 | } 985 | 986 | nts1_status_t nts1_req_ampeg_desc(uint8_t idx) { 987 | nts1_tx_event_t event; 988 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 989 | event.msb = k_param_id_ampeg_type; 990 | event.lsb = idx & 0x7F; 991 | return nts1_send_event(&event); 992 | } 993 | 994 | nts1_status_t nts1_req_mod_count(void) { 995 | nts1_tx_event_t event; 996 | event.event_id = k_nts1_tx_event_id_req_unit_count; 997 | event.msb = k_param_id_mod_type; 998 | event.lsb = 0x0; 999 | return nts1_send_event(&event); 1000 | } 1001 | 1002 | nts1_status_t nts1_req_mod_desc(uint8_t idx) { 1003 | nts1_tx_event_t event; 1004 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 1005 | event.msb = k_param_id_mod_type; 1006 | event.lsb = idx & 0x7F; 1007 | return nts1_send_event(&event); 1008 | } 1009 | 1010 | nts1_status_t nts1_req_del_count(void) { 1011 | nts1_tx_event_t event; 1012 | event.event_id = k_nts1_tx_event_id_req_unit_count; 1013 | event.msb = k_param_id_del_type; 1014 | event.lsb = 0x0; 1015 | return nts1_send_event(&event); 1016 | } 1017 | 1018 | nts1_status_t nts1_req_del_desc(uint8_t idx) { 1019 | nts1_tx_event_t event; 1020 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 1021 | event.msb = k_param_id_del_type; 1022 | event.lsb = idx & 0x7F; 1023 | return nts1_send_event(&event); 1024 | } 1025 | 1026 | nts1_status_t nts1_req_rev_count(void) { 1027 | nts1_tx_event_t event; 1028 | event.event_id = k_nts1_tx_event_id_req_unit_count; 1029 | event.msb = k_param_id_rev_type; 1030 | event.lsb = 0x0; 1031 | return nts1_send_event(&event); 1032 | } 1033 | 1034 | nts1_status_t nts1_req_rev_desc(uint8_t idx) { 1035 | nts1_tx_event_t event; 1036 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 1037 | event.msb = k_param_id_rev_type; 1038 | event.lsb = idx & 0x7F; 1039 | return nts1_send_event(&event); 1040 | } 1041 | 1042 | nts1_status_t nts1_req_arp_pattern_count(void) { 1043 | nts1_tx_event_t event; 1044 | event.event_id = k_nts1_tx_event_id_req_unit_count; 1045 | event.msb = k_param_id_arp_pattern; 1046 | event.lsb = 0x0; 1047 | return nts1_send_event(&event); 1048 | } 1049 | 1050 | nts1_status_t nts1_req_arp_pattern_desc(uint8_t idx) { 1051 | nts1_tx_event_t event; 1052 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 1053 | event.msb = k_param_id_arp_pattern; 1054 | event.lsb = idx & 0x7F; 1055 | return nts1_send_event(&event); 1056 | } 1057 | 1058 | nts1_status_t nts1_req_arp_intervals_count(void) { 1059 | nts1_tx_event_t event; 1060 | event.event_id = k_nts1_tx_event_id_req_unit_count; 1061 | event.msb = k_param_id_arp_intervals; 1062 | event.lsb = 0x0; 1063 | return nts1_send_event(&event); 1064 | } 1065 | 1066 | nts1_status_t nts1_req_arp_intervals_desc(uint8_t idx) { 1067 | nts1_tx_event_t event; 1068 | event.event_id = k_nts1_tx_event_id_req_unit_desc; 1069 | event.msb = k_param_id_arp_intervals; 1070 | event.lsb = idx & 0x7F; 1071 | return nts1_send_event(&event); 1072 | } 1073 | 1074 | 1075 | // ---------------------------------------------------- 1076 | -------------------------------------------------------------------------------- /docs/nts-1-customizations/variants/NTS1_REF_CP_REVC/nts1_iface.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nts1_iface.h 3 | * @brief C API handling communication with the NTS-1 digital kit's main board. 4 | * 5 | * Provides initialization of necessary peripherals, handling of low level 6 | * protocol details and convenience functions for bidrectional communication 7 | * with the NTS-1 digital kit's main board via SPI. 8 | * 9 | * BSD 3-Clause License 10 | * Copyright (c) 2020, KORG INC. 11 | * All rights reserved. 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * * Redistributions of source code must retain the above copyright notice, this 15 | * list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * * Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived from 21 | * this software without specific prior written permission. 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | //*/ 33 | 34 | 35 | #ifndef __nts1_iface_h 36 | #define __nts1_iface_h 37 | 38 | #include 39 | 40 | enum { 41 | k_nts1_status_ok = 0x00U, 42 | k_nts1_status_error = 0x01U, 43 | k_nts1_status_busy = 0x02U, 44 | k_nts1_status_timeout = 0x03U, 45 | }; 46 | 47 | typedef uint8_t nts1_status_t; 48 | 49 | enum { 50 | k_nts1_tx_event_id_note_off = 0x00U, 51 | k_nts1_tx_event_id_note_on = 0x01U, 52 | k_nts1_tx_event_id_req_unit_count = 0x10U, 53 | k_nts1_tx_event_id_req_unit_desc = 0x11U, 54 | k_nts1_tx_event_id_req_edit_param_desc = 0x12U, 55 | k_nts1_tx_event_id_req_value = 0x13U, 56 | }; 57 | 58 | typedef uint8_t nts1_tx_event_id_t; 59 | 60 | enum { 61 | k_nts1_rx_event_id_note_off = 0x0U, 62 | k_nts1_rx_event_id_note_on = 0x1U, 63 | k_nts1_rx_event_id_step_tick = 0x2U, 64 | k_nts1_rx_event_id_unit_desc = 0x11U, 65 | k_nts1_rx_event_id_edit_param_desc = 0x12U, 66 | k_nts1_rx_event_id_value = 0x13U, 67 | }; 68 | 69 | typedef uint8_t nts1_rx_event_id_t; 70 | 71 | enum { 72 | // osc 73 | k_param_id_osc_base = 0U, 74 | k_param_id_osc_type = k_param_id_osc_base, 75 | k_param_id_osc_shape, 76 | k_param_id_osc_shift_shape, 77 | k_param_id_osc_lfo_rate, 78 | k_param_id_osc_lfo_depth, 79 | k_param_id_osc_edit, // with subid 80 | k_param_id_osc_last = k_param_id_osc_edit, 81 | 82 | // ampeg 83 | k_param_id_ampeg_base, 84 | k_param_id_ampeg_type = k_param_id_ampeg_base, 85 | k_param_id_ampeg_attack, 86 | k_param_id_ampeg_release, 87 | k_param_id_ampeg_lfo_rate, 88 | k_param_id_ampeg_lfo_depth, 89 | k_param_id_ampeg_unset3, 90 | k_param_id_ampeg_last = k_param_id_ampeg_unset3, 91 | 92 | // filter 93 | k_param_id_filt_base, 94 | k_param_id_filt_type = k_param_id_filt_base, 95 | k_param_id_filt_cutoff, 96 | k_param_id_filt_peak, 97 | k_param_id_filt_lfo_rate, 98 | k_param_id_filt_lfo_depth, 99 | k_param_id_filt_unset3, 100 | k_param_id_filt_last = k_param_id_filt_unset3, 101 | 102 | // modulation effect 103 | k_param_id_mod_base, 104 | k_param_id_mod_type = k_param_id_mod_base, 105 | k_param_id_mod_time, 106 | k_param_id_mod_depth, 107 | k_param_id_mod_unset1, 108 | k_param_id_mod_unset2, 109 | k_param_id_mod_unset3, 110 | k_param_id_mod_last = k_param_id_mod_unset3, 111 | 112 | // delay effect 113 | k_param_id_del_base, 114 | k_param_id_del_type = k_param_id_del_base, 115 | k_param_id_del_time, 116 | k_param_id_del_depth, 117 | k_param_id_del_unset1, 118 | k_param_id_del_mix, 119 | k_param_id_del_unset3, 120 | k_param_id_del_last = k_param_id_del_unset3, 121 | 122 | // reverb effect 123 | k_param_id_rev_base, 124 | k_param_id_rev_type = k_param_id_rev_base, 125 | k_param_id_rev_time, 126 | k_param_id_rev_depth, 127 | k_param_id_rev_unset1, 128 | k_param_id_rev_mix, 129 | k_param_id_rev_unset3, 130 | k_param_id_rev_last = k_param_id_rev_unset3, 131 | 132 | // arpeggiator 133 | k_param_id_arp_base, 134 | k_param_id_arp_pattern = k_param_id_arp_base, 135 | k_param_id_arp_intervals, 136 | k_param_id_arp_length, 137 | k_param_id_arp_state, 138 | k_param_id_arp_tempo, 139 | k_param_id_arp_last = k_param_id_arp_tempo, 140 | 141 | k_num_param_id, 142 | 143 | // dummy param ids for system values 144 | k_param_id_sys_base = k_num_param_id, 145 | k_param_id_sys_version = k_param_id_sys_base, 146 | k_param_id_sys_global, 147 | k_param_id_sys_last = k_param_id_sys_global, 148 | 149 | k_num_all_param_id, 150 | 151 | k_param_id_invalid = 0x7FU, 152 | 153 | k_num_osc_param_id = k_param_id_osc_last - k_param_id_osc_base, 154 | k_num_ampeg_param_id = k_param_id_ampeg_last - k_param_id_ampeg_base, 155 | k_num_filt_param_id = k_param_id_filt_last - k_param_id_filt_base, 156 | k_num_mod_param_id = k_param_id_mod_last - k_param_id_mod_base, 157 | k_num_del_param_id = k_param_id_del_last - k_param_id_del_base, 158 | k_num_rev_param_id = k_param_id_rev_last - k_param_id_rev_base, 159 | k_num_arp_param_id = k_param_id_arp_last - k_param_id_arp_base, 160 | k_num_sys_param_id = k_param_id_sys_last - k_param_id_sys_base, 161 | }; 162 | 163 | typedef uint8_t param_main_id_t; 164 | 165 | enum { 166 | // osc edit parameters 167 | k_param_subid_osc_base = 0U, 168 | k_param_subid_osc_edit1 = k_param_subid_osc_base, 169 | k_param_subid_osc_edit2, 170 | k_param_subid_osc_edit3, 171 | k_param_subid_osc_edit4, 172 | k_param_subid_osc_edit5, 173 | k_param_subid_osc_edit6, 174 | k_param_subid_osc_last = k_param_subid_osc_edit6, 175 | k_num_osc_param_subid, 176 | }; 177 | 178 | enum { 179 | // global parameters 180 | k_param_subid_sys_global_base = 0, 181 | k_param_subid_sys_global_input_route = k_param_subid_sys_global_base, 182 | k_param_subid_sys_global_input_trim, 183 | k_param_subid_sys_global_syncout_polarity, 184 | k_param_subid_sys_global_syncin_polarity, 185 | k_param_subid_sys_global_tempo_range, 186 | k_param_subid_sys_global_clock_source, 187 | k_param_subid_sys_global_short_message, 188 | k_param_subid_sys_global_midi_route, 189 | k_param_subid_sys_global_midi_channel, 190 | k_param_subid_sys_global_sync_step, 191 | k_param_subid_sys_global_last = k_param_subid_sys_global_sync_step, 192 | k_num_sys_global_param_subid, 193 | }; 194 | 195 | #define k_invalid_param_subid 0xF 196 | 197 | typedef uint8_t param_sub_id_t; 198 | 199 | enum { 200 | k_edit_param_type_percent = 0, 201 | k_edit_param_type_percent_bipolar, 202 | k_edit_param_type_select, 203 | }; 204 | 205 | #define param_id_get_main(id) ((id)>>8) 206 | #define param_id_get_sub(id) ((id)&0x0F) 207 | #define make_param_id(main, sub) ((((main) & 0x7F)<<8)|((sub) & 0x0F)) 208 | 209 | typedef uint16_t param_id_t; 210 | 211 | #define k_param_id_none 0x7FFF 212 | #define k_param_id_invalid 0x7FFF 213 | 214 | 215 | typedef struct nts1_tx_event { 216 | uint8_t event_id; 217 | uint8_t msb; // 7 bit 218 | uint8_t lsb; // 7 bit 219 | } nts1_tx_event_t; 220 | 221 | typedef struct nts1_tx_param_change { 222 | uint8_t param_id; 223 | uint8_t param_subid; 224 | uint8_t msb; // 7 bit 225 | uint8_t lsb; // 7 bit 226 | } nts1_tx_param_change_t; 227 | 228 | typedef struct nts1_rx_event_header { 229 | uint8_t size; // size of whole message incl. this header 230 | uint8_t event_id; 231 | // events are of variable size 232 | // data payload is encoded in 7 bit words 233 | } nts1_rx_event_header_t; 234 | 235 | typedef struct nts1_rx_param_change { 236 | uint8_t param_id; 237 | uint8_t param_subid; 238 | uint8_t msb; // 7 bit 239 | uint8_t lsb; // 7 bit 240 | } nts1_rx_param_change_t; 241 | 242 | typedef struct { 243 | uint8_t note; 244 | uint8_t velocity; 245 | } nts1_rx_note_on_t; 246 | 247 | typedef struct { 248 | uint8_t note; 249 | uint8_t padding; 250 | } nts1_rx_note_off_t; 251 | 252 | typedef struct { 253 | uint8_t req_id; 254 | uint8_t main_id; 255 | uint8_t sub_id; 256 | uint8_t padding; 257 | uint16_t value; 258 | uint8_t padding2[10]; 259 | } nts1_rx_value_t; 260 | 261 | typedef struct { 262 | uint8_t main_id; 263 | uint8_t sub_id; 264 | uint8_t param_count; 265 | char name[13]; 266 | } nts1_rx_unit_desc_t; 267 | 268 | typedef struct { 269 | uint8_t main_id; 270 | uint8_t sub_id; 271 | uint8_t value_type; 272 | int8_t min; 273 | int8_t max; 274 | char name[13]; 275 | } nts1_rx_edit_param_desc_t; 276 | 277 | typedef void (*nts1_note_off_event_handler)(const nts1_rx_note_off_t *); 278 | typedef void (*nts1_note_on_event_handler)(const nts1_rx_note_on_t *); 279 | typedef void (*nts1_step_tick_event_handler)(void); 280 | typedef void (*nts1_unit_desc_event_handler)(const nts1_rx_unit_desc_t *); 281 | typedef void (*nts1_edit_param_desc_event_handler)(const nts1_rx_edit_param_desc_t *); 282 | typedef void (*nts1_value_event_handler)(const nts1_rx_value_t *); 283 | typedef void (*nts1_param_change_handler)(const nts1_rx_param_change_t *); 284 | 285 | #ifdef __cplusplus 286 | extern "C" { 287 | #endif 288 | 289 | nts1_status_t nts1_init(); 290 | nts1_status_t nts1_teardown(); 291 | nts1_status_t nts1_idle(); 292 | 293 | nts1_status_t nts1_send_events(nts1_tx_event_t *events, uint8_t count); 294 | 295 | static inline nts1_status_t nts1_send_event(nts1_tx_event_t *event) { 296 | return nts1_send_events(event, 1); 297 | } 298 | 299 | nts1_status_t nts1_send_param_changes(nts1_tx_param_change_t *param_changes, uint8_t count); 300 | 301 | static inline nts1_status_t nts1_send_param_change(nts1_tx_param_change_t *param_change) { 302 | return nts1_send_param_changes(param_change, 1); 303 | } 304 | 305 | static inline uint32_t nts1_size_7to8(uint32_t size7) { 306 | return 7 * (size7 / 8) + size7%8 - 1; 307 | } 308 | 309 | static inline uint32_t nts1_size_8to7(uint32_t size8) { 310 | return 8 * (size8 / 7) + size8%7 + 1; 311 | } 312 | 313 | uint32_t nts1_convert_7to8(uint8_t *dest8, const uint8_t *src7, uint32_t size7); 314 | uint32_t nts1_convert_8to7(uint8_t *dest7, const uint8_t *src8, uint32_t size8); 315 | 316 | nts1_status_t nts1_param_change(uint8_t id, uint8_t subid, uint16_t value); 317 | 318 | nts1_status_t nts1_note_on(uint8_t note, uint8_t velo); 319 | nts1_status_t nts1_note_off(uint8_t note); 320 | 321 | nts1_status_t nts1_req_sys_version(void); 322 | 323 | nts1_status_t nts1_req_param_value(uint8_t id, uint8_t subid); 324 | 325 | nts1_status_t nts1_req_osc_count(void); 326 | nts1_status_t nts1_req_osc_desc(uint8_t idx); 327 | nts1_status_t nts1_req_osc_edit_param_desc(uint8_t idx); 328 | 329 | nts1_status_t nts1_req_filt_count(void); 330 | nts1_status_t nts1_req_filt_desc(uint8_t idx); 331 | 332 | nts1_status_t nts1_req_ampeg_count(void); 333 | nts1_status_t nts1_req_ampeg_desc(uint8_t idx); 334 | 335 | nts1_status_t nts1_req_mod_count(void); 336 | nts1_status_t nts1_req_mod_desc(uint8_t idx); 337 | 338 | nts1_status_t nts1_req_del_count(void); 339 | nts1_status_t nts1_req_del_desc(uint8_t idx); 340 | 341 | nts1_status_t nts1_req_rev_count(void); 342 | nts1_status_t nts1_req_rev_desc(uint8_t idx); 343 | 344 | nts1_status_t nts1_req_arp_pattern_count(void); 345 | nts1_status_t nts1_req_arp_pattern_desc(uint8_t idx); 346 | 347 | nts1_status_t nts1_req_arp_intervals_count(void); 348 | nts1_status_t nts1_req_arp_intervals_desc(uint8_t idx); 349 | 350 | // RX Event handlers, weakly defined in C++ NTS1 object. 351 | void nts1_handle_note_off_event(const nts1_rx_note_off_t *note_off); 352 | void nts1_handle_note_on_event(const nts1_rx_note_on_t *note_on); 353 | void nts1_handle_step_tick_event(void); 354 | void nts1_handle_unit_desc_event(const nts1_rx_unit_desc_t *unit_desc); 355 | void nts1_handle_edit_param_desc_event(const nts1_rx_edit_param_desc_t *param_desc); 356 | void nts1_handle_value_event(const nts1_rx_value_t *value); 357 | void nts1_handle_param_change(const nts1_rx_param_change_t *param_change); 358 | 359 | #ifdef __cplusplus 360 | } 361 | #endif 362 | 363 | #endif // __nts1_iface_h 364 | 365 | -------------------------------------------------------------------------------- /examples/0-log-received.main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/semphr.h" 9 | #include "freertos/queue.h" 10 | 11 | // #include "lwip/sockets.h" 12 | // #include "lwip/dns.h" 13 | // #include "lwip/netdb.h" 14 | // #include "lwip/igmp.h" 15 | 16 | //#include "esp_wifi.h" 17 | #include "esp_system.h" 18 | #include "esp_event.h" 19 | #include "nvs_flash.h" 20 | #include "soc/rtc_periph.h" 21 | #include "driver/spi_slave.h" 22 | #include "esp_log.h" 23 | #include "esp_spi_flash.h" 24 | #include "driver/gpio.h" 25 | 26 | /* 27 | SPI receiver (slave) example. 28 | 29 | This example is supposed to work together with the SPI sender. It uses the standard SPI pins (MISO, MOSI, SCLK, CS) to 30 | transmit data over in a full-duplex fashion, that is, while the master puts data on the MOSI pin, the slave puts its own 31 | data on the MISO pin. 32 | 33 | This example uses one extra pin: GPIO_HANDSHAKE is used as a handshake pin. After a transmission has been set up and we're 34 | ready to send/receive data, this code uses a callback to set the handshake pin high. The sender will detect this and start 35 | sending a transaction. As soon as the transaction is done, the line gets set low again. 36 | */ 37 | 38 | /* 39 | Pins in use. The SPI Master can use the GPIO mux, so feel free to change these if needed. 40 | */ 41 | #include "nts1_iface.h" 42 | 43 | // ---------------------------------------------------- 44 | 45 | enum { 46 | k_tx_cmd_event = 0x84U, 47 | k_tx_cmd_param = 0x85U, 48 | k_tx_cmd_other = 0x86U, 49 | k_tx_cmd_dummy = 0x87U 50 | }; 51 | 52 | enum { 53 | k_tx_subcmd_other_ack = 0x3U, 54 | k_tx_subcmd_other_version = 0x10U, 55 | k_tx_subcmd_other_bootmode = 0x11U, 56 | }; 57 | 58 | enum { 59 | k_rx_cmd_event = 0x84U, 60 | k_rx_cmd_param = 0x85U, 61 | k_rx_cmd_other = 0x86U, 62 | k_rx_cmd_dummy = 0x87U 63 | }; 64 | 65 | enum { 66 | k_rx_subcmd_other_panelid = 0x0U, 67 | k_rx_subcmd_other_stsreq = 0x1U, 68 | k_rx_subcmd_other_ackreq = 0x3U, 69 | }; 70 | 71 | typedef struct __nts1_cmd_header { 72 | uint8_t cmd:3; 73 | uint8_t panel_id:3; 74 | uint8_t end_mark:1; 75 | uint8_t start_bit:1; 76 | } __nts1_cmd_header_t; 77 | 78 | // ---------------------------------------------------- 79 | 80 | 81 | #define SPI_MODE 3 82 | #define SPI_BITORDER SPI_SLAVE_BIT_LSBFIRST 83 | #define GPIO_MOSI 23 // G 84 | #define GPIO_HANDSHAKE 16 // R 85 | #define GPIO_MISO 19 // O 86 | #define GPIO_SCLK 18 // Y 87 | #define GPIO_CS 5 // - None 88 | 89 | #ifdef CONFIG_IDF_TARGET_ESP32 90 | 91 | #define RCV_HOST VSPI_HOST 92 | // disable dma, use direct spi buffer 93 | #define DMA_CHAN 0 94 | 95 | #elif defined CONFIG_IDF_TARGET_ESP32S2 96 | #define RCV_HOST SPI2_HOST 97 | #define DMA_CHAN RCV_HOST 98 | 99 | #endif 100 | 101 | #define BLINK_GPIO 13 102 | 103 | 104 | #define SPI_TX_BUF_SIZE (32) 105 | #define SPI_TX_BUF_MASK (SPI_TX_BUF_SIZE - 1) 106 | 107 | #define SPI_RX_BUF_SIZE (32) 108 | #define SPI_RX_BUF_MASK (SPI_RX_BUF_SIZE - 1) 109 | 110 | static uint8_t s_spi_tx_buf[SPI_TX_BUF_SIZE]; 111 | static uint16_t s_spi_tx_ridx; // Read Index (from s_spi_tx_buf) 112 | static uint16_t s_spi_tx_widx; // Write Index (to s_spi_tx_buf) 113 | 114 | static uint8_t s_spi_rx_buf[SPI_RX_BUF_SIZE]; 115 | static uint16_t s_spi_rx_ridx; // Read Index (from s_spi_rx_buf) 116 | static uint16_t s_spi_rx_widx; // Write Index (to s_spi_rx_buf) 117 | 118 | #define SPI_BUF_INC(idx, bufSize) (((idx+1) == bufSize) ? 0 : idx + 1) 119 | 120 | #define SPI_TX_BUF_RESET() (s_spi_tx_ridx = s_spi_tx_widx = 0) 121 | #define SPI_TX_BUF_EMPTY() ((SPI_TX_BUF_MASK & s_spi_tx_ridx) == (SPI_TX_BUF_MASK & s_spi_tx_widx)) 122 | #define SPI_RX_BUF_RESET() (s_spi_rx_ridx = s_spi_rx_widx = 0) 123 | #define SPI_RX_BUF_EMPTY() ((SPI_RX_BUF_MASK & s_spi_rx_ridx) == (SPI_RX_BUF_MASK & s_spi_rx_widx)) 124 | 125 | #define PANEL_ID_MASK 0x38 // Bits 3-5 126 | #define PANEL_CMD_EMARK 0x40 // Bit 6 127 | #define PANEL_START_BIT 0x80 // Bit 7 128 | 129 | static uint8_t s_panel_id = PANEL_ID_MASK; // Bits 3-5 "ppp"="111" 130 | static uint8_t s_dummy_tx_cmd = (PANEL_ID_MASK + 0xC7); // B'11ppp111; 131 | 132 | 133 | typedef uint8_t nts1_status_t; 134 | 135 | // Configures a GPIO pin for ACK (acknowledge message) 136 | void gpio_init() { 137 | //Configuration for the handshake line 138 | gpio_config_t io_conf={ 139 | .mode=GPIO_MODE_OUTPUT, 140 | .intr_type=GPIO_INTR_DISABLE, 141 | .pin_bit_mask=(1< 2 | #include 3 | #include 4 | #include 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/semphr.h" 9 | #include "freertos/queue.h" 10 | 11 | // #include "lwip/sockets.h" 12 | // #include "lwip/dns.h" 13 | // #include "lwip/netdb.h" 14 | // #include "lwip/igmp.h" 15 | 16 | //#include "esp_wifi.h" 17 | #include "esp_system.h" 18 | #include "esp_event.h" 19 | #include "nvs_flash.h" 20 | #include "soc/rtc_periph.h" 21 | #include "driver/spi_slave.h" 22 | #include "esp_log.h" 23 | #include "esp_spi_flash.h" 24 | #include "driver/gpio.h" 25 | 26 | /* 27 | SPI receiver (slave) example. 28 | 29 | This example is supposed to work together with the SPI sender. It uses the standard SPI pins (MISO, MOSI, SCLK, CS) to 30 | transmit data over in a full-duplex fashion, that is, while the master puts data on the MOSI pin, the slave puts its own 31 | data on the MISO pin. 32 | 33 | This example uses one extra pin: GPIO_HANDSHAKE is used as a handshake pin. After a transmission has been set up and we're 34 | ready to send/receive data, this code uses a callback to set the handshake pin high. The sender will detect this and start 35 | sending a transaction. As soon as the transaction is done, the line gets set low again. 36 | */ 37 | 38 | /* 39 | Pins in use. The SPI Master can use the GPIO mux, so feel free to change these if needed. 40 | */ 41 | #include "nts1_iface.h" 42 | 43 | // ---------------------------------------------------- 44 | 45 | enum { 46 | k_tx_cmd_event = 0x84U, 47 | k_tx_cmd_param = 0x85U, 48 | k_tx_cmd_other = 0x86U, 49 | k_tx_cmd_dummy = 0x87U 50 | }; 51 | 52 | enum { 53 | k_tx_subcmd_other_ack = 0x3U, 54 | k_tx_subcmd_other_version = 0x10U, 55 | k_tx_subcmd_other_bootmode = 0x11U, 56 | }; 57 | 58 | enum { 59 | k_rx_cmd_event = 0x84U, 60 | k_rx_cmd_param = 0x85U, 61 | k_rx_cmd_other = 0x86U, 62 | k_rx_cmd_dummy = 0x87U 63 | }; 64 | 65 | enum { 66 | k_rx_subcmd_other_panelid = 0x0U, 67 | k_rx_subcmd_other_stsreq = 0x1U, 68 | k_rx_subcmd_other_ackreq = 0x3U, 69 | }; 70 | 71 | typedef struct __nts1_cmd_header { 72 | uint8_t cmd:3; 73 | uint8_t panel_id:3; 74 | uint8_t end_mark:1; 75 | uint8_t start_bit:1; 76 | } __nts1_cmd_header_t; 77 | 78 | // ---------------------------------------------------- 79 | 80 | 81 | #define SPI_MODE 3 82 | #define SPI_BITORDER SPI_SLAVE_BIT_LSBFIRST 83 | #define GPIO_MOSI 23 // G 84 | #define GPIO_HANDSHAKE 16 // R 85 | #define GPIO_MISO 19 // O 86 | #define GPIO_SCLK 18 // Y 87 | #define GPIO_CS 5 // - None 88 | 89 | #ifdef CONFIG_IDF_TARGET_ESP32 90 | 91 | #define RCV_HOST VSPI_HOST 92 | // disable dma, use direct spi buffer 93 | #define DMA_CHAN 0 94 | 95 | #elif defined CONFIG_IDF_TARGET_ESP32S2 96 | #define RCV_HOST SPI2_HOST 97 | #define DMA_CHAN RCV_HOST 98 | 99 | #endif 100 | 101 | #define BLINK_GPIO 13 102 | 103 | 104 | #define SPI_TX_BUF_SIZE (32) 105 | #define SPI_TX_BUF_MASK (SPI_TX_BUF_SIZE - 1) 106 | 107 | #define SPI_RX_BUF_SIZE (32) 108 | #define SPI_RX_BUF_MASK (SPI_RX_BUF_SIZE - 1) 109 | 110 | static uint8_t s_spi_tx_buf[SPI_TX_BUF_SIZE]; 111 | static uint16_t s_spi_tx_ridx; // Read Index (from s_spi_tx_buf) 112 | static uint16_t s_spi_tx_widx; // Write Index (to s_spi_tx_buf) 113 | 114 | static uint8_t s_spi_rx_buf[SPI_RX_BUF_SIZE]; 115 | static uint16_t s_spi_rx_ridx; // Read Index (from s_spi_rx_buf) 116 | static uint16_t s_spi_rx_widx; // Write Index (to s_spi_rx_buf) 117 | 118 | #define SPI_TX_BUF_RESET() (s_spi_tx_ridx = s_spi_tx_widx = 0) 119 | #define SPI_TX_BUF_EMPTY() ((SPI_TX_BUF_MASK & s_spi_tx_ridx) == (SPI_TX_BUF_MASK & s_spi_tx_widx)) 120 | #define SPI_RX_BUF_RESET() (s_spi_rx_ridx = s_spi_rx_widx = 0) 121 | #define SPI_RX_BUF_EMPTY() ((SPI_RX_BUF_MASK & s_spi_rx_ridx) == (SPI_RX_BUF_MASK & s_spi_rx_widx)) 122 | 123 | #define PANEL_ID_MASK 0x38 // Bits 3-5 124 | #define PANEL_CMD_EMARK 0x40 // Bit 6 125 | #define PANEL_START_BIT 0x80 // Bit 7 126 | 127 | static uint8_t s_panel_id = PANEL_ID_MASK; // Bits 3-5 "ppp"="111" 128 | static uint8_t s_dummy_tx_cmd = (PANEL_ID_MASK + 0xC7); // B'11ppp111; 129 | 130 | 131 | typedef uint8_t nts1_status_t; 132 | 133 | // Configures a GPIO pin for ACK (acknowledge message) 134 | void gpio_init() { 135 | //Configuration for the handshake line 136 | gpio_config_t io_conf={ 137 | .mode=GPIO_MODE_OUTPUT, 138 | .intr_type=GPIO_INTR_DISABLE, 139 | .pin_bit_mask=(1< 2 | #include 3 | #include 4 | #include 5 | 6 | #include "freertos/FreeRTOS.h" 7 | #include "freertos/task.h" 8 | #include "freertos/semphr.h" 9 | #include "freertos/queue.h" 10 | 11 | // #include "lwip/sockets.h" 12 | // #include "lwip/dns.h" 13 | // #include "lwip/netdb.h" 14 | // #include "lwip/igmp.h" 15 | 16 | //#include "esp_wifi.h" 17 | #include "esp_system.h" 18 | #include "esp_event.h" 19 | #include "nvs_flash.h" 20 | #include "soc/rtc_periph.h" 21 | #include "driver/spi_slave.h" 22 | #include "esp_log.h" 23 | #include "esp_spi_flash.h" 24 | #include "driver/gpio.h" 25 | 26 | /* 27 | SPI receiver (slave) example. 28 | 29 | This example is supposed to work together with the SPI sender. It uses the standard SPI pins (MISO, MOSI, SCLK, CS) to 30 | transmit data over in a full-duplex fashion, that is, while the master puts data on the MOSI pin, the slave puts its own 31 | data on the MISO pin. 32 | 33 | This example uses one extra pin: GPIO_HANDSHAKE is used as a handshake pin. After a transmission has been set up and we're 34 | ready to send/receive data, this code uses a callback to set the handshake pin high. The sender will detect this and start 35 | sending a transaction. As soon as the transaction is done, the line gets set low again. 36 | */ 37 | 38 | /* 39 | Pins in use. The SPI Master can use the GPIO mux, so feel free to change these if needed. 40 | */ 41 | #include "nts1_iface.h" 42 | 43 | // ---------------------------------------------------- 44 | 45 | enum { 46 | k_tx_cmd_event = 0x84U, 47 | k_tx_cmd_param = 0x85U, 48 | k_tx_cmd_other = 0x86U, 49 | k_tx_cmd_dummy = 0x87U 50 | }; 51 | 52 | enum { 53 | k_tx_subcmd_other_ack = 0x3U, 54 | k_tx_subcmd_other_version = 0x10U, 55 | k_tx_subcmd_other_bootmode = 0x11U, 56 | }; 57 | 58 | enum { 59 | k_rx_cmd_event = 0x84U, 60 | k_rx_cmd_param = 0x85U, 61 | k_rx_cmd_other = 0x86U, 62 | k_rx_cmd_dummy = 0x87U 63 | }; 64 | 65 | enum { 66 | k_rx_subcmd_other_panelid = 0x0U, 67 | k_rx_subcmd_other_stsreq = 0x1U, 68 | k_rx_subcmd_other_ackreq = 0x3U, 69 | }; 70 | 71 | typedef struct __nts1_cmd_header { 72 | uint8_t cmd:3; 73 | uint8_t panel_id:3; 74 | uint8_t end_mark:1; 75 | uint8_t start_bit:1; 76 | } __nts1_cmd_header_t; 77 | 78 | // ---------------------------------------------------- 79 | 80 | 81 | #define SPI_MODE 3 82 | #define SPI_BITORDER SPI_SLAVE_BIT_LSBFIRST 83 | #define GPIO_SCLK 18 // SCK 84 | #define GPIO_MOSI 23 // RX_PANEL 85 | #define GPIO_MISO 19 // TX_PANEL 86 | #define GPIO_HANDSHAKE 21 // ACK 87 | #define GPIO_CS 5 // - None 88 | #define SPI_FRAME_SIZE 8 89 | 90 | #ifdef CONFIG_IDF_TARGET_ESP32 91 | 92 | #define RCV_HOST VSPI_HOST 93 | // disable dma, use direct spi buffer 94 | #define DMA_CHAN 0 95 | 96 | #elif defined CONFIG_IDF_TARGET_ESP32S2 97 | #define RCV_HOST SPI2_HOST 98 | #define DMA_CHAN RCV_HOST 99 | 100 | #endif 101 | 102 | #define BLINK_GPIO 13 103 | 104 | 105 | #define SPI_TX_BUF_SIZE (32) 106 | #define SPI_TX_BUF_MASK (SPI_TX_BUF_SIZE - 1) 107 | 108 | #define SPI_RX_BUF_SIZE (32) 109 | #define SPI_RX_BUF_MASK (SPI_RX_BUF_SIZE - 1) 110 | 111 | WORD_ALIGNED_ATTR uint8_t s_spi_tx_buf[SPI_TX_BUF_SIZE]; 112 | WORD_ALIGNED_ATTR uint8_t s_spi_rx_buf[SPI_RX_BUF_SIZE]; 113 | 114 | static uint16_t s_spi_tx_ridx; // Read Index (from s_spi_tx_buf) 115 | static uint16_t s_spi_tx_widx; // Write Index (to s_spi_tx_buf) 116 | 117 | static uint16_t s_spi_rx_ridx; // Read Index (from s_spi_rx_buf) 118 | static uint16_t s_spi_rx_widx; // Write Index (to s_spi_rx_buf) 119 | 120 | #define SPI_BUF_INC(idx, bufSize) (((idx+1) == bufSize) ? 0 : idx + 1) 121 | 122 | #define SPI_TX_BUF_RESET() (s_spi_tx_ridx = s_spi_tx_widx = 0) 123 | #define SPI_TX_BUF_EMPTY() ((SPI_TX_BUF_MASK & s_spi_tx_ridx) == (SPI_TX_BUF_MASK & s_spi_tx_widx)) 124 | #define SPI_RX_BUF_RESET() (s_spi_rx_ridx = s_spi_rx_widx = 0) 125 | #define SPI_RX_BUF_EMPTY() ((SPI_RX_BUF_MASK & s_spi_rx_ridx) == (SPI_RX_BUF_MASK & s_spi_rx_widx)) 126 | 127 | #define PANEL_ID_MASK 0x38 // Bits 3-5 128 | #define PANEL_CMD_EMARK 0x40 // Bit 6 129 | #define PANEL_START_BIT 0x80 // Bit 7 130 | 131 | static uint8_t s_panel_id = PANEL_ID_MASK; // Bits 3-5 "ppp"="111" 132 | static uint8_t s_dummy_tx_cmd = (PANEL_ID_MASK + 0xC7); // B'11ppp111; 133 | 134 | 135 | typedef uint8_t nts1_status_t; 136 | 137 | // Configures a GPIO pin for ACK (acknowledge message) 138 | void gpio_init() { 139 | //Configuration for the handshake line 140 | gpio_config_t io_conf={ 141 | .mode=GPIO_MODE_OUTPUT, 142 | .intr_type=GPIO_INTR_DISABLE, 143 | .pin_bit_mask=(1<=2) { 223 | s_spi_tx_buf[0] = 196; // 11000100 224 | s_spi_tx_buf[1] = 1; 225 | s_spi_tx_buf[2] = note; 226 | s_spi_tx_buf[3] = 80; 227 | 228 | 229 | // if we get to the max number, change the direction 230 | if (note == 127) { 231 | increase = 0; 232 | } 233 | if (note == 20) { 234 | increase = 1; 235 | } 236 | // increase or decrease the note number 237 | note = increase ? note + 1 : note - 1; 238 | } 239 | 240 | //Set up a transaction of 128 bytes to send/receive 241 | t.length=SPI_RX_BUF_SIZE*4; 242 | t.tx_buffer=s_spi_tx_buf; 243 | t.rx_buffer=s_spi_rx_buf; 244 | 245 | /* This call enables the SPI slave interface to send/receive to the sendbuf and recvbuf. The transaction is 246 | initialized by the SPI master, however, so it will not actually happen until the master starts a hardware transaction 247 | by pulling CS low and pulsing the clock etc. In this specific example, we use the handshake line, pulled up by the 248 | .post_setup_cb callback that is called as soon as a transaction is ready, to let the master know it is free to transfer 249 | data. 250 | */ 251 | ret=spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY); 252 | 253 | //spi_slave_transmit does not return until the master has done a transmission, so by here we have sent our data and 254 | //received data from the master. Print it. 255 | uint8_t* cp = s_spi_rx_buf; 256 | for (uint8_t i = 0; i < SPI_RX_BUF_SIZE; ++cp) 257 | { 258 | printf("%02x", *cp); 259 | i += 1; 260 | } 261 | printf("\n\n"); 262 | 263 | n++; 264 | } 265 | 266 | } 267 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/**/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = esp32 13 | 14 | [env:esp32] 15 | board = esp32thing 16 | framework = espidf 17 | platform = espressif32 18 | monitor_speed = 115200 19 | monitor_filters = esp32_exception_decoder 20 | build_type = debug 21 | 22 | [env:stm32] 23 | platform = ststm32 24 | board = nucleo_f030r8 25 | framework = arduino 26 | build_type = debug 27 | src_filter = +<*.[ch]> +<*.cpp> - 28 | -------------------------------------------------------------------------------- /sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file. DO NOT EDIT. 3 | # Espressif IoT Development Framework (ESP-IDF) Project Configuration 4 | # 5 | CONFIG_IDF_TARGET_ESP32=y 6 | CONFIG_IDF_TARGET="esp32" 7 | CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 8 | 9 | # 10 | # SDK tool configuration 11 | # 12 | CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" 13 | CONFIG_APP_COMPILE_TIME_DATE=y 14 | # CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set 15 | # CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set 16 | CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 17 | # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set 18 | # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set 19 | # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set 20 | CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y 21 | # CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set 22 | # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set 23 | CONFIG_BOOTLOADER_LOG_LEVEL=3 24 | # CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set 25 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 26 | # CONFIG_BOOTLOADER_FACTORY_RESET is not set 27 | # CONFIG_BOOTLOADER_APP_TEST is not set 28 | CONFIG_BOOTLOADER_WDT_ENABLE=y 29 | # CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set 30 | CONFIG_BOOTLOADER_WDT_TIME_MS=9000 31 | # CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set 32 | # CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set 33 | # CONFIG_SECURE_BOOT_ENABLED is not set 34 | # CONFIG_SECURE_FLASH_ENC_ENABLED is not set 35 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 36 | # CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set 37 | # CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set 38 | CONFIG_ESPTOOLPY_FLASHMODE_DIO=y 39 | # CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set 40 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 41 | # CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set 42 | CONFIG_ESPTOOLPY_FLASHFREQ_40M=y 43 | # CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set 44 | # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set 45 | CONFIG_ESPTOOLPY_FLASHFREQ="40m" 46 | # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set 47 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y 48 | # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set 49 | # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set 50 | # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set 51 | CONFIG_ESPTOOLPY_FLASHSIZE="2MB" 52 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 53 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 54 | # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set 55 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 56 | CONFIG_ESPTOOLPY_AFTER_RESET=y 57 | # CONFIG_ESPTOOLPY_AFTER_NORESET is not set 58 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 59 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set 60 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set 61 | CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y 62 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set 63 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set 64 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set 65 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set 66 | CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 67 | CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 68 | CONFIG_PARTITION_TABLE_SINGLE_APP=y 69 | # CONFIG_PARTITION_TABLE_TWO_OTA is not set 70 | # CONFIG_PARTITION_TABLE_CUSTOM is not set 71 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 72 | CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" 73 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 74 | CONFIG_PARTITION_TABLE_MD5=y 75 | CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y 76 | # CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set 77 | CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y 78 | # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set 79 | # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set 80 | # CONFIG_COMPILER_CXX_EXCEPTIONS is not set 81 | CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y 82 | # CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set 83 | # CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set 84 | # CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set 85 | # CONFIG_COMPILER_STACK_CHECK is not set 86 | # CONFIG_COMPILER_WARN_WRITE_STRINGS is not set 87 | # CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set 88 | # CONFIG_ESP32_APPTRACE_DEST_TRAX is not set 89 | CONFIG_ESP32_APPTRACE_DEST_NONE=y 90 | # CONFIG_ESP32_APPTRACE_ENABLE is not set 91 | CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y 92 | # CONFIG_BT_ENABLED is not set 93 | CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=0 94 | # CONFIG_BTDM_CTRL_AUTO_LATENCY_EFF is not set 95 | CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=0 96 | CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=0 97 | CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=0 98 | CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 99 | CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1 100 | CONFIG_BT_RESERVE_DRAM=0 101 | # CONFIG_BLE_MESH is not set 102 | # CONFIG_ADC_FORCE_XPD_FSM is not set 103 | CONFIG_ADC_DISABLE_DAC=y 104 | # CONFIG_SPI_MASTER_IN_IRAM is not set 105 | CONFIG_SPI_MASTER_ISR_IN_IRAM=y 106 | # CONFIG_SPI_SLAVE_IN_IRAM is not set 107 | CONFIG_SPI_SLAVE_ISR_IN_IRAM=y 108 | # CONFIG_EFUSE_CUSTOM_TABLE is not set 109 | # CONFIG_EFUSE_VIRTUAL is not set 110 | # CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set 111 | CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y 112 | # CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set 113 | CONFIG_EFUSE_MAX_BLK_LEN=192 114 | # CONFIG_ESP_TLS_SERVER is not set 115 | CONFIG_ESP32_REV_MIN_0=y 116 | # CONFIG_ESP32_REV_MIN_1 is not set 117 | # CONFIG_ESP32_REV_MIN_2 is not set 118 | # CONFIG_ESP32_REV_MIN_3 is not set 119 | CONFIG_ESP32_REV_MIN=0 120 | CONFIG_ESP32_DPORT_WORKAROUND=y 121 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set 122 | CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y 123 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set 124 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 125 | # CONFIG_ESP32_SPIRAM_SUPPORT is not set 126 | # CONFIG_ESP32_MEMMAP_TRACEMEM is not set 127 | # CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS is not set 128 | # CONFIG_ESP32_TRAX is not set 129 | CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 130 | # CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set 131 | CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y 132 | CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 133 | # CONFIG_ESP32_ULP_COPROC_ENABLED is not set 134 | CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 135 | # CONFIG_ESP32_PANIC_PRINT_HALT is not set 136 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 137 | # CONFIG_ESP32_PANIC_SILENT_REBOOT is not set 138 | # CONFIG_ESP32_PANIC_GDBSTUB is not set 139 | CONFIG_ESP32_DEBUG_OCDAWARE=y 140 | CONFIG_ESP32_DEBUG_STUBS_ENABLE=y 141 | CONFIG_ESP32_BROWNOUT_DET=y 142 | CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y 143 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set 144 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set 145 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set 146 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set 147 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set 148 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set 149 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set 150 | CONFIG_ESP32_BROWNOUT_DET_LVL=0 151 | CONFIG_ESP32_REDUCE_PHY_TX_POWER=y 152 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 153 | # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set 154 | # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set 155 | # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set 156 | CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y 157 | # CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set 158 | # CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set 159 | # CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set 160 | CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 161 | CONFIG_ESP32_RTC_XTAL_CAL_RETRY=1 162 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 163 | CONFIG_ESP32_XTAL_FREQ_40=y 164 | # CONFIG_ESP32_XTAL_FREQ_26 is not set 165 | # CONFIG_ESP32_XTAL_FREQ_AUTO is not set 166 | CONFIG_ESP32_XTAL_FREQ=40 167 | # CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set 168 | # CONFIG_ESP32_NO_BLOBS is not set 169 | # CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set 170 | # CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set 171 | CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 172 | # CONFIG_PM_ENABLE is not set 173 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 174 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 175 | CONFIG_ADC_CAL_LUT_ENABLE=y 176 | # CONFIG_ESP_TIMER_PROFILING is not set 177 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 178 | CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 179 | CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 180 | CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 181 | CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 182 | CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 183 | CONFIG_ESP_CONSOLE_UART_DEFAULT=y 184 | # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set 185 | # CONFIG_ESP_CONSOLE_UART_NONE is not set 186 | CONFIG_ESP_CONSOLE_UART_NUM=0 187 | CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 188 | CONFIG_ESP_INT_WDT=y 189 | CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 190 | CONFIG_ESP_INT_WDT_CHECK_CPU1=y 191 | CONFIG_ESP_TASK_WDT=y 192 | # CONFIG_ESP_TASK_WDT_PANIC is not set 193 | CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 194 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 195 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y 196 | CONFIG_ETH_USE_ESP32_EMAC=y 197 | CONFIG_ETH_PHY_INTERFACE_RMII=y 198 | # CONFIG_ETH_PHY_INTERFACE_MII is not set 199 | CONFIG_ETH_RMII_CLK_INPUT=y 200 | # CONFIG_ETH_RMII_CLK_OUTPUT is not set 201 | CONFIG_ETH_RMII_CLK_IN_GPIO=0 202 | CONFIG_ETH_DMA_BUFFER_SIZE=512 203 | CONFIG_ETH_DMA_RX_BUFFER_NUM=10 204 | CONFIG_ETH_DMA_TX_BUFFER_NUM=10 205 | CONFIG_ETH_USE_SPI_ETHERNET=y 206 | CONFIG_ETH_SPI_ETHERNET_DM9051=y 207 | # CONFIG_ESP_EVENT_LOOP_PROFILING is not set 208 | CONFIG_ESP_EVENT_POST_FROM_ISR=y 209 | CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y 210 | CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y 211 | # CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set 212 | CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 213 | CONFIG_HTTPD_MAX_URI_LEN=512 214 | CONFIG_HTTPD_ERR_RESP_NO_DELAY=y 215 | CONFIG_HTTPD_PURGE_BUF_LEN=32 216 | # CONFIG_HTTPD_LOG_PURGE_DATA is not set 217 | # CONFIG_OTA_ALLOW_HTTP is not set 218 | # CONFIG_ESP_HTTPS_SERVER_ENABLE is not set 219 | CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 220 | CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 221 | # CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set 222 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y 223 | CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 224 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 225 | # CONFIG_ESP32_WIFI_CSI_ENABLED is not set 226 | CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y 227 | CONFIG_ESP32_WIFI_TX_BA_WIN=6 228 | CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y 229 | CONFIG_ESP32_WIFI_RX_BA_WIN=6 230 | CONFIG_ESP32_WIFI_NVS_ENABLED=y 231 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y 232 | # CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set 233 | CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 234 | CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 235 | # CONFIG_ESP32_WIFI_DEBUG_LOG_ENABLE is not set 236 | CONFIG_ESP32_WIFI_IRAM_OPT=y 237 | CONFIG_ESP32_WIFI_RX_IRAM_OPT=y 238 | CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y 239 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 240 | # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set 241 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 242 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 243 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set 244 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set 245 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 246 | # CONFIG_ESP32_ENABLE_COREDUMP is not set 247 | # CONFIG_FATFS_CODEPAGE_DYNAMIC is not set 248 | CONFIG_FATFS_CODEPAGE_437=y 249 | # CONFIG_FATFS_CODEPAGE_720 is not set 250 | # CONFIG_FATFS_CODEPAGE_737 is not set 251 | # CONFIG_FATFS_CODEPAGE_771 is not set 252 | # CONFIG_FATFS_CODEPAGE_775 is not set 253 | # CONFIG_FATFS_CODEPAGE_850 is not set 254 | # CONFIG_FATFS_CODEPAGE_852 is not set 255 | # CONFIG_FATFS_CODEPAGE_855 is not set 256 | # CONFIG_FATFS_CODEPAGE_857 is not set 257 | # CONFIG_FATFS_CODEPAGE_860 is not set 258 | # CONFIG_FATFS_CODEPAGE_861 is not set 259 | # CONFIG_FATFS_CODEPAGE_862 is not set 260 | # CONFIG_FATFS_CODEPAGE_863 is not set 261 | # CONFIG_FATFS_CODEPAGE_864 is not set 262 | # CONFIG_FATFS_CODEPAGE_865 is not set 263 | # CONFIG_FATFS_CODEPAGE_866 is not set 264 | # CONFIG_FATFS_CODEPAGE_869 is not set 265 | # CONFIG_FATFS_CODEPAGE_932 is not set 266 | # CONFIG_FATFS_CODEPAGE_936 is not set 267 | # CONFIG_FATFS_CODEPAGE_949 is not set 268 | # CONFIG_FATFS_CODEPAGE_950 is not set 269 | CONFIG_FATFS_CODEPAGE=437 270 | CONFIG_FATFS_LFN_NONE=y 271 | # CONFIG_FATFS_LFN_HEAP is not set 272 | # CONFIG_FATFS_LFN_STACK is not set 273 | CONFIG_FATFS_FS_LOCK=0 274 | CONFIG_FATFS_TIMEOUT_MS=10000 275 | CONFIG_FATFS_PER_FILE_CACHE=y 276 | CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 277 | CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 278 | CONFIG_FMB_QUEUE_LENGTH=20 279 | CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048 280 | CONFIG_FMB_SERIAL_BUF_SIZE=256 281 | CONFIG_FMB_SERIAL_TASK_PRIO=10 282 | # CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set 283 | CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 284 | CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 285 | CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 286 | CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 287 | CONFIG_FMB_TIMER_PORT_ENABLED=y 288 | CONFIG_FMB_TIMER_GROUP=0 289 | CONFIG_FMB_TIMER_INDEX=0 290 | # CONFIG_FREERTOS_UNICORE is not set 291 | CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF 292 | CONFIG_FREERTOS_CORETIMER_0=y 293 | # CONFIG_FREERTOS_CORETIMER_1 is not set 294 | CONFIG_FREERTOS_HZ=100 295 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 296 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set 297 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set 298 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y 299 | # CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set 300 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y 301 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 302 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 303 | # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set 304 | # CONFIG_FREERTOS_ASSERT_DISABLE is not set 305 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 306 | CONFIG_FREERTOS_ISR_STACKSIZE=1536 307 | # CONFIG_FREERTOS_LEGACY_HOOKS is not set 308 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 309 | # CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION is not set 310 | CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 311 | CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 312 | CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 313 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 314 | # CONFIG_FREERTOS_USE_TRACE_FACILITY is not set 315 | # CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set 316 | # CONFIG_FREERTOS_DEBUG_INTERNALS is not set 317 | CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y 318 | CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y 319 | # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set 320 | CONFIG_HEAP_POISONING_DISABLED=y 321 | # CONFIG_HEAP_POISONING_LIGHT is not set 322 | # CONFIG_HEAP_POISONING_COMPREHENSIVE is not set 323 | CONFIG_HEAP_TRACING_OFF=y 324 | # CONFIG_HEAP_TRACING_STANDALONE is not set 325 | # CONFIG_HEAP_TRACING_TOHOST is not set 326 | # CONFIG_HEAP_TRACING is not set 327 | # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set 328 | # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set 329 | # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set 330 | # CONFIG_LOG_DEFAULT_LEVEL_INFO is not set 331 | # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set 332 | CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=y 333 | CONFIG_LOG_DEFAULT_LEVEL=5 334 | CONFIG_LOG_COLORS=y 335 | CONFIG_LWIP_LOCAL_HOSTNAME="espressif" 336 | # CONFIG_LWIP_L2_TO_L3_COPY is not set 337 | # CONFIG_LWIP_IRAM_OPTIMIZATION is not set 338 | CONFIG_LWIP_TIMERS_ONDEMAND=y 339 | CONFIG_LWIP_MAX_SOCKETS=10 340 | # CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set 341 | CONFIG_LWIP_SO_REUSE=y 342 | CONFIG_LWIP_SO_REUSE_RXTOALL=y 343 | # CONFIG_LWIP_SO_RCVBUF is not set 344 | CONFIG_LWIP_IP_FRAG=y 345 | # CONFIG_LWIP_IP_REASSEMBLY is not set 346 | # CONFIG_LWIP_STATS is not set 347 | # CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set 348 | CONFIG_LWIP_ESP_GRATUITOUS_ARP=y 349 | CONFIG_LWIP_GARP_TMR_INTERVAL=60 350 | CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 351 | CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y 352 | # CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set 353 | CONFIG_LWIP_DHCPS_LEASE_UNIT=60 354 | CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 355 | # CONFIG_LWIP_AUTOIP is not set 356 | # CONFIG_LWIP_IPV6_AUTOCONFIG is not set 357 | CONFIG_LWIP_NETIF_LOOPBACK=y 358 | CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 359 | CONFIG_LWIP_MAX_ACTIVE_TCP=16 360 | CONFIG_LWIP_MAX_LISTENING_TCP=16 361 | CONFIG_LWIP_TCP_MAXRTX=12 362 | CONFIG_LWIP_TCP_SYNMAXRTX=6 363 | CONFIG_LWIP_TCP_MSS=1440 364 | CONFIG_LWIP_TCP_MSL=60000 365 | CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 366 | CONFIG_LWIP_TCP_WND_DEFAULT=5744 367 | CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 368 | CONFIG_LWIP_TCP_QUEUE_OOSEQ=y 369 | # CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set 370 | CONFIG_LWIP_TCP_OVERSIZE_MSS=y 371 | # CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set 372 | # CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set 373 | CONFIG_LWIP_MAX_UDP_PCBS=16 374 | CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 375 | CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 376 | CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y 377 | # CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set 378 | # CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set 379 | CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF 380 | # CONFIG_LWIP_PPP_SUPPORT is not set 381 | # CONFIG_LWIP_MULTICAST_PING is not set 382 | # CONFIG_LWIP_BROADCAST_PING is not set 383 | CONFIG_LWIP_MAX_RAW_PCBS=16 384 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 385 | CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 386 | CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y 387 | # CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set 388 | # CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set 389 | CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y 390 | CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 391 | CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 392 | # CONFIG_MBEDTLS_DEBUG is not set 393 | # CONFIG_MBEDTLS_ECP_RESTARTABLE is not set 394 | # CONFIG_MBEDTLS_CMAC_C is not set 395 | CONFIG_MBEDTLS_HARDWARE_AES=y 396 | # CONFIG_MBEDTLS_HARDWARE_MPI is not set 397 | CONFIG_MBEDTLS_HARDWARE_SHA=y 398 | CONFIG_MBEDTLS_HAVE_TIME=y 399 | # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set 400 | CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y 401 | # CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set 402 | # CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set 403 | # CONFIG_MBEDTLS_TLS_DISABLED is not set 404 | CONFIG_MBEDTLS_TLS_SERVER=y 405 | CONFIG_MBEDTLS_TLS_CLIENT=y 406 | CONFIG_MBEDTLS_TLS_ENABLED=y 407 | # CONFIG_MBEDTLS_PSK_MODES is not set 408 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y 409 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y 410 | CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y 411 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y 412 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y 413 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y 414 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y 415 | CONFIG_MBEDTLS_SSL_RENEGOTIATION=y 416 | # CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set 417 | CONFIG_MBEDTLS_SSL_PROTO_TLS1=y 418 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y 419 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y 420 | # CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set 421 | CONFIG_MBEDTLS_SSL_ALPN=y 422 | CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y 423 | CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y 424 | CONFIG_MBEDTLS_AES_C=y 425 | # CONFIG_MBEDTLS_CAMELLIA_C is not set 426 | # CONFIG_MBEDTLS_DES_C is not set 427 | CONFIG_MBEDTLS_RC4_DISABLED=y 428 | # CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set 429 | # CONFIG_MBEDTLS_RC4_ENABLED is not set 430 | # CONFIG_MBEDTLS_BLOWFISH_C is not set 431 | # CONFIG_MBEDTLS_XTEA_C is not set 432 | CONFIG_MBEDTLS_CCM_C=y 433 | CONFIG_MBEDTLS_GCM_C=y 434 | # CONFIG_MBEDTLS_RIPEMD160_C is not set 435 | CONFIG_MBEDTLS_PEM_PARSE_C=y 436 | CONFIG_MBEDTLS_PEM_WRITE_C=y 437 | CONFIG_MBEDTLS_X509_CRL_PARSE_C=y 438 | CONFIG_MBEDTLS_X509_CSR_PARSE_C=y 439 | CONFIG_MBEDTLS_ECP_C=y 440 | CONFIG_MBEDTLS_ECDH_C=y 441 | CONFIG_MBEDTLS_ECDSA_C=y 442 | CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y 443 | CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y 444 | CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y 445 | CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y 446 | CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y 447 | CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y 448 | CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y 449 | CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y 450 | CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y 451 | CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y 452 | CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y 453 | CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y 454 | CONFIG_MBEDTLS_ECP_NIST_OPTIM=y 455 | CONFIG_MDNS_MAX_SERVICES=10 456 | CONFIG_MQTT_PROTOCOL_311=y 457 | CONFIG_MQTT_TRANSPORT_SSL=y 458 | CONFIG_MQTT_TRANSPORT_WEBSOCKET=y 459 | CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y 460 | # CONFIG_MQTT_USE_CUSTOM_CONFIG is not set 461 | # CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set 462 | # CONFIG_MQTT_CUSTOM_OUTBOX is not set 463 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 464 | # CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set 465 | # CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set 466 | # CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set 467 | # CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set 468 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y 469 | # CONFIG_NEWLIB_NANO_FORMAT is not set 470 | # CONFIG_OPENSSL_DEBUG is not set 471 | # CONFIG_OPENSSL_ASSERT_DO_NOTHING is not set 472 | CONFIG_OPENSSL_ASSERT_EXIT=y 473 | CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 474 | CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 475 | CONFIG_PTHREAD_STACK_MIN=768 476 | CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y 477 | # CONFIG_PTHREAD_DEFAULT_CORE_0 is not set 478 | # CONFIG_PTHREAD_DEFAULT_CORE_1 is not set 479 | CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 480 | CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" 481 | # CONFIG_SPI_FLASH_VERIFY_WRITE is not set 482 | # CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set 483 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 484 | CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y 485 | # CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set 486 | # CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set 487 | # CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set 488 | CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y 489 | CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y 490 | CONFIG_SPIFFS_MAX_PARTITIONS=3 491 | CONFIG_SPIFFS_CACHE=y 492 | CONFIG_SPIFFS_CACHE_WR=y 493 | # CONFIG_SPIFFS_CACHE_STATS is not set 494 | CONFIG_SPIFFS_PAGE_CHECK=y 495 | CONFIG_SPIFFS_GC_MAX_RUNS=10 496 | # CONFIG_SPIFFS_GC_STATS is not set 497 | CONFIG_SPIFFS_PAGE_SIZE=256 498 | CONFIG_SPIFFS_OBJ_NAME_LEN=32 499 | CONFIG_SPIFFS_USE_MAGIC=y 500 | CONFIG_SPIFFS_USE_MAGIC_LENGTH=y 501 | CONFIG_SPIFFS_META_LENGTH=4 502 | CONFIG_SPIFFS_USE_MTIME=y 503 | # CONFIG_SPIFFS_DBG is not set 504 | # CONFIG_SPIFFS_API_DBG is not set 505 | # CONFIG_SPIFFS_GC_DBG is not set 506 | # CONFIG_SPIFFS_CACHE_DBG is not set 507 | # CONFIG_SPIFFS_CHECK_DBG is not set 508 | # CONFIG_SPIFFS_TEST_VISUALISATION is not set 509 | CONFIG_NETIF_IP_LOST_TIMER_INTERVAL=120 510 | CONFIG_TCPIP_LWIP=y 511 | CONFIG_UNITY_ENABLE_FLOAT=y 512 | CONFIG_UNITY_ENABLE_DOUBLE=y 513 | # CONFIG_UNITY_ENABLE_COLOR is not set 514 | CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y 515 | # CONFIG_UNITY_ENABLE_FIXTURE is not set 516 | # CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set 517 | CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y 518 | CONFIG_VFS_SUPPORT_TERMIOS=y 519 | CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 520 | CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 521 | # CONFIG_WL_SECTOR_SIZE_512 is not set 522 | CONFIG_WL_SECTOR_SIZE_4096=y 523 | CONFIG_WL_SECTOR_SIZE=4096 524 | CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 525 | CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 526 | CONFIG_WPA_MBEDTLS_CRYPTO=y 527 | # CONFIG_WPA_TLS_V12 is not set 528 | # CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set 529 | 530 | # Deprecated options for backward compatibility 531 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 532 | # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set 533 | # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set 534 | # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set 535 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y 536 | # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set 537 | # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set 538 | CONFIG_LOG_BOOTLOADER_LEVEL=3 539 | # CONFIG_APP_ROLLBACK_ENABLE is not set 540 | # CONFIG_FLASH_ENCRYPTION_ENABLED is not set 541 | # CONFIG_FLASHMODE_QIO is not set 542 | # CONFIG_FLASHMODE_QOUT is not set 543 | CONFIG_FLASHMODE_DIO=y 544 | # CONFIG_FLASHMODE_DOUT is not set 545 | # CONFIG_MONITOR_BAUD_9600B is not set 546 | # CONFIG_MONITOR_BAUD_57600B is not set 547 | CONFIG_MONITOR_BAUD_115200B=y 548 | # CONFIG_MONITOR_BAUD_230400B is not set 549 | # CONFIG_MONITOR_BAUD_921600B is not set 550 | # CONFIG_MONITOR_BAUD_2MB is not set 551 | # CONFIG_MONITOR_BAUD_OTHER is not set 552 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 553 | CONFIG_MONITOR_BAUD=115200 554 | CONFIG_OPTIMIZATION_LEVEL_DEBUG=y 555 | # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set 556 | CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y 557 | # CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set 558 | # CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set 559 | # CONFIG_CXX_EXCEPTIONS is not set 560 | CONFIG_STACK_CHECK_NONE=y 561 | # CONFIG_STACK_CHECK_NORM is not set 562 | # CONFIG_STACK_CHECK_STRONG is not set 563 | # CONFIG_STACK_CHECK_ALL is not set 564 | # CONFIG_STACK_CHECK is not set 565 | # CONFIG_WARN_WRITE_STRINGS is not set 566 | # CONFIG_DISABLE_GCC8_WARNINGS is not set 567 | CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=0 568 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=0 569 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=0 570 | CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 571 | CONFIG_ADC2_DISABLE_DAC=y 572 | # CONFIG_SPIRAM_SUPPORT is not set 573 | # CONFIG_MEMMAP_TRACEMEM is not set 574 | # CONFIG_MEMMAP_TRACEMEM_TWOBANKS is not set 575 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 576 | # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set 577 | CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y 578 | CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 579 | # CONFIG_ULP_COPROC_ENABLED is not set 580 | CONFIG_ULP_COPROC_RESERVE_MEM=0 581 | CONFIG_BROWNOUT_DET=y 582 | CONFIG_BROWNOUT_DET_LVL_SEL_0=y 583 | # CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set 584 | # CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set 585 | # CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set 586 | # CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set 587 | # CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set 588 | # CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set 589 | # CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set 590 | CONFIG_BROWNOUT_DET_LVL=0 591 | CONFIG_REDUCE_PHY_TX_POWER=y 592 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 593 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set 594 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set 595 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set 596 | # CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set 597 | # CONFIG_NO_BLOBS is not set 598 | # CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set 599 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 600 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 601 | CONFIG_MAIN_TASK_STACK_SIZE=3584 602 | CONFIG_IPC_TASK_STACK_SIZE=1024 603 | CONFIG_TIMER_TASK_STACK_SIZE=3584 604 | CONFIG_CONSOLE_UART_DEFAULT=y 605 | # CONFIG_CONSOLE_UART_CUSTOM is not set 606 | # CONFIG_CONSOLE_UART_NONE is not set 607 | CONFIG_CONSOLE_UART_NUM=0 608 | CONFIG_CONSOLE_UART_BAUDRATE=115200 609 | CONFIG_INT_WDT=y 610 | CONFIG_INT_WDT_TIMEOUT_MS=300 611 | CONFIG_INT_WDT_CHECK_CPU1=y 612 | CONFIG_TASK_WDT=y 613 | # CONFIG_TASK_WDT_PANIC is not set 614 | CONFIG_TASK_WDT_TIMEOUT_S=5 615 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 616 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y 617 | # CONFIG_EVENT_LOOP_PROFILING is not set 618 | CONFIG_POST_EVENTS_FROM_ISR=y 619 | CONFIG_POST_EVENTS_FROM_IRAM_ISR=y 620 | CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 621 | CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 622 | CONFIG_MB_QUEUE_LENGTH=20 623 | CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048 624 | CONFIG_MB_SERIAL_BUF_SIZE=256 625 | CONFIG_MB_SERIAL_TASK_PRIO=10 626 | # CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set 627 | CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 628 | CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 629 | CONFIG_MB_CONTROLLER_STACK_SIZE=4096 630 | CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 631 | CONFIG_MB_TIMER_PORT_ENABLED=y 632 | CONFIG_MB_TIMER_GROUP=0 633 | CONFIG_MB_TIMER_INDEX=0 634 | # CONFIG_SUPPORT_STATIC_ALLOCATION is not set 635 | CONFIG_TIMER_TASK_PRIORITY=1 636 | CONFIG_TIMER_TASK_STACK_DEPTH=2048 637 | CONFIG_TIMER_QUEUE_LENGTH=10 638 | # CONFIG_L2_TO_L3_COPY is not set 639 | # CONFIG_USE_ONLY_LWIP_SELECT is not set 640 | CONFIG_ESP_GRATUITOUS_ARP=y 641 | CONFIG_GARP_TMR_INTERVAL=60 642 | CONFIG_TCPIP_RECVMBOX_SIZE=32 643 | CONFIG_TCP_MAXRTX=12 644 | CONFIG_TCP_SYNMAXRTX=6 645 | CONFIG_TCP_MSS=1440 646 | CONFIG_TCP_MSL=60000 647 | CONFIG_TCP_SND_BUF_DEFAULT=5744 648 | CONFIG_TCP_WND_DEFAULT=5744 649 | CONFIG_TCP_RECVMBOX_SIZE=6 650 | CONFIG_TCP_QUEUE_OOSEQ=y 651 | # CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set 652 | CONFIG_TCP_OVERSIZE_MSS=y 653 | # CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set 654 | # CONFIG_TCP_OVERSIZE_DISABLE is not set 655 | CONFIG_UDP_RECVMBOX_SIZE=6 656 | CONFIG_TCPIP_TASK_STACK_SIZE=3072 657 | CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y 658 | # CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set 659 | # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set 660 | CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF 661 | # CONFIG_PPP_SUPPORT is not set 662 | CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 663 | CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 664 | CONFIG_ESP32_PTHREAD_STACK_MIN=768 665 | CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y 666 | # CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set 667 | # CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set 668 | CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 669 | CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" 670 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y 671 | # CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set 672 | # CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set 673 | CONFIG_IP_LOST_TIMER_INTERVAL=120 674 | CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y 675 | CONFIG_SUPPORT_TERMIOS=y 676 | # End of deprecated options 677 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.c) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "freertos/FreeRTOSConfig.h" 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "freertos/semphr.h" 11 | #include "freertos/queue.h" 12 | 13 | // #include "lwip/sockets.h" 14 | // #include "lwip/dns.h" 15 | // #include "lwip/netdb.h" 16 | // #include "lwip/igmp.h" 17 | 18 | #include "esp_system.h" 19 | #include "bootloader_random.h" 20 | #include "esp_event.h" 21 | #include "nvs_flash.h" 22 | #include "soc/rtc_periph.h" 23 | #include "esp_log.h" 24 | #include "esp_spi_flash.h" 25 | 26 | #include "driver/gpio.h" 27 | #include "nts1_iface.h" 28 | 29 | #define BLINK_GPIO GPIO_NUM_13 30 | 31 | void blink() 32 | { 33 | gpio_set_level(BLINK_GPIO, 1); 34 | vTaskDelay(10 / portTICK_PERIOD_MS); 35 | gpio_set_level(BLINK_GPIO, 0); 36 | vTaskDelete(NULL); 37 | } 38 | 39 | void psts(char *message, uint8_t sts) 40 | { 41 | printf(message); 42 | printf(";; status: %d\n", sts); 43 | } 44 | 45 | // RX Event handlers, weakly defined in C++ NTS1 object. 46 | void nts1_handle_note_off_event(const nts1_rx_note_off_t *note_off) 47 | { 48 | // printf("note_off %d\n", note_off->note); 49 | } 50 | void nts1_handle_note_on_event(const nts1_rx_note_on_t *note_on) 51 | { 52 | // printf("note_on %d\n", note_on->note); 53 | } 54 | void nts1_handle_step_tick_event(void) 55 | { 56 | // printf("tick\n"); 57 | xTaskCreate( 58 | blink, /* Task function. */ 59 | "another Task", /* name of task. */ 60 | 10000, /* Stack size of task */ 61 | NULL, /* parameter of the task */ 62 | 1, /* priority of the task */ 63 | NULL); /* Task handle to keep track of created task */ 64 | } 65 | 66 | void nts1_handle_unit_desc_event(const nts1_rx_unit_desc_t *unit_desc) 67 | { 68 | printf("unit_desc %s %d %d %d\n", 69 | unit_desc->name, 70 | unit_desc->main_id, 71 | unit_desc->sub_id, 72 | unit_desc->param_count); 73 | } 74 | void nts1_handle_edit_param_desc_event(const nts1_rx_edit_param_desc_t *param_desc) 75 | { 76 | printf("edit_param %s %d %d %d %d %d\n", 77 | param_desc->name, 78 | param_desc->main_id, 79 | param_desc->sub_id, 80 | param_desc->value_type, 81 | param_desc->min, 82 | param_desc->max); 83 | } 84 | void nts1_handle_value_event(const nts1_rx_value_t *value) 85 | { 86 | printf("value %d %d %d %d %d\n", 87 | value->req_id, 88 | value->main_id, 89 | value->sub_id, 90 | value->padding, 91 | value->value); 92 | } 93 | void nts1_handle_param_change(const nts1_rx_param_change_t *param_change) 94 | { 95 | printf("param_change %d %d %d %d\n", 96 | param_change->param_id, 97 | param_change->param_subid, 98 | param_change->msb, 99 | param_change->lsb); 100 | } 101 | 102 | void app_main(void) 103 | { 104 | uint8_t init_status = nts1_init(); 105 | bootloader_random_enable(); 106 | 107 | gpio_reset_pin(BLINK_GPIO); 108 | gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); 109 | 110 | uint8_t note = 0; 111 | uint8_t increase = 1; 112 | uint8_t velo = 100; 113 | uint8_t param_val = 0; 114 | 115 | int n = 0; 116 | while (1) 117 | { 118 | n++; 119 | // psts("idle", nts1_idle()); 120 | nts1_idle(); 121 | if (n != 10000) 122 | { 123 | continue; 124 | } 125 | n = 0; 126 | 127 | //psts("note on", nts1_note_on(note, velo)); 128 | 129 | // if we get to the max number, change the direction 130 | if (note >= 127) 131 | { 132 | increase = 0; 133 | // nts1_req_ampeg_count(); 134 | } 135 | else if (note <= 20) 136 | { 137 | increase = 1; 138 | } 139 | else 140 | { 141 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit1, param_val); 142 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit2, param_val); 143 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit3, param_val); 144 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit4, param_val); 145 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit5, param_val); 146 | nts1_param_change(k_param_id_osc_base, k_param_subid_osc_edit6, param_val); 147 | param_val = esp_random() % 126; 148 | 149 | nts1_note_on(note, velo); 150 | } 151 | // increase or decrease the note number 152 | note = increase ? note + 1 : note - 1; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Blank Template for NTS-1 Custom Panel reference board rev.B 3 | 4 | This is free and unencumbered software released into the public domain. 5 | 6 | Anyone is free to copy, modify, publish, use, compile, sell, or 7 | distribute this software, either in source code form or as a compiled 8 | binary, for any purpose, commercial or non-commercial, and by any 9 | means. 10 | 11 | In jurisdictions that recognize copyright laws, the author or authors 12 | of this software dedicate any and all copyright interest in the 13 | software to the public domain. We make this dedication for the benefit 14 | of the public at large and to the detriment of our heirs and 15 | successors. We intend this dedication to be an overt act of 16 | relinquishment in perpetuity of all present and future rights to this 17 | software under copyright law. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | For more information, please refer to 28 | //*/ 29 | 30 | #include 31 | 32 | // --- NTS-1 main board interface ------------------------------------------------------------------ 33 | #include 34 | 35 | NTS1 nts1; 36 | 37 | // --- Arduino Sketch Entry ------------------------------------------------------------------------ 38 | 39 | void setup() 40 | { 41 | nts1.init(); 42 | 43 | // put your setup code here, to run once: 44 | } 45 | 46 | // --- Arduino Sketch Idle Loop ---------------------------------------------------------------------- 47 | 48 | void loop() 49 | { 50 | nts1.idle(); 51 | 52 | // put your main code here, to run repeatedly: 53 | } 54 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------