Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

USB serial: maybe a clogged buffer?#3235

JoshuaACNewman started this conversation inGeneral
Discussion options

I'm trying to track down a weird problem, and it's possible the problem is a constipated USB serial buffer.

The objective: communicate between an RPi Pico2 and a patch running inplugdata. The Pico2 is mounted on a PCB called an Oblique Palette with 8 i2c DACs and 8 multiplexed channels running to one ADC, plus of course their support electronics.
You can see it running here.

Expected Behavior:

  1. The plugdata patch running on a computer opens the serial port.
  2. The µC (the Palette) reports its configuration as a confirmation of the connection.
  3. The patch receives the configuration, makes it visible.
  4. The patch sends over a packet of 8 delimited values, one for each channel.
  5. The Palette receives the values, sets the DACs to mirror the values from the patch, then reads all the ADC channels and sends them to the patch.
  6. When the patch receives the values from the Palette, the patch sends its output values again.
  7. Repeat steps 4-7 until your creative objectives are realized.

What happens:

MacOS: Works okay, but at a sample rate of 40Hz, which is pretty damn slow considering the speed of communication I should be getting. I don't see why I should be getting slower than kilohertz sample rates. Note: I had incorrect pullup values earlier, and the result before that was that it would communicate OK at ~40Hz, then, after a random number of seconds, the Palette would start waiting exactly 4 seconds (minus about 100 µsecs) before continuing.

Linux: Works great for a random number of seconds at ~350Hz, then stops altogether, neither reading nor writing, and the LED status light, which should be switching each time it completes a task, is completely solid, indicating that the µC is not running at all. If I try to close the serial port from the computer side, plugdata hangs, as it is waiting to hear back from the completely frozen serial port. Unplugging the USB immediately restores the patch, and plugging the board back in brings it back to life as normal.

This has been driving me bonkers for almost two months now, and I've solved what seems to be half the problem (needing to switch to surprisingly small i2c pullup resistors), but now something (and I suspect the serial port) is stopping me.

Man, I'd love some help.

Code follows

// Oblique Palette// Communication of values in ASCII between the Easel and the Palette.// The Palette (this firmware) speaks with the Easel abstraction patch in plugdata, running on a separate GP computer.// It announces its presence with its name, version number, and hardware configuration.// The Easel responds with output values whenever it gets DAC values from the Palette.// The Palette responds back ADC values, (later, followed by the measured time between communications, to the Easel)// The Palette updates all DACs// ( The Palette adjusts its sample rate according to the round trip time)// ( The Easel adjusts it sample rate according to the round trip time)// #include <Adafruit_TinyUSB.h>// High efficiency USB serial communication#include <Arduino.h>           // Implicit in the Arduino IDE#include <Mux.h>               // Multiplexer for analog inputs#include <Servo.h>             // For servo-range PWM controls#include <Wire.h>              // i2c communication for DACs#include <Adafruit_MCP4725.h>  // i2c DACs#include <pico/stdlib.h>       // To access Pico-specific libraries#include <hardware/adc.h>      // Direct ADC read access for high speed reads#include <hardware/gpio.h>     // Direct GPIO access#include "Adafruit_TinyUSB.h"  // Better USB communicationusing namespace admux;  // for ADC multiplexer/**********************************Hardware configuration of the Palette***********************************/const char modelName[] = "Palette";       // Model nameconst char firmwareVersion[] = "v0.5.6";  // Version number of this firmwareconst int DACBitDepth = 12;               // Output/DAC resolutionconst int numOutputChannels = 8;          // Number of output channels on the Paletteconst int ADCBitDepth = 12;               // Input/ADC resolutionconst int numInputChannels = 8;           // Number of input channels on the Palettechar specsheet[32];                       // Buffer to carry the specsheet string to the Easelconst int numSamplesPerChannel = 1;  // How many samples to pack for transmission (for increasing sample rate later)const int maxDACValue = (pow(2, DACBitDepth) - 1);  // The maximum DAC valueconst int maxADCValue = (pow(2, ADCBitDepth) - 1);  // The maximum ADC valueconst int numValuesFromEaselPerPacket = numOutputChannels * numSamplesPerChannel;const int numValuesToEaselPerPacket = numInputChannels * numSamplesPerChannel;const byte numi2cBusses = 2;  // Using i2c busses 0 and 1/*********************************Analytics*********************************/long lastTime;                          // For holding the time to measure the amount of time processing takesconst int communicationTimeout = 1000;  // How long to wait to hear backlong communicationTimeoutStart;         // If communication goes down, start a timerbool ledState = 0;/*********************************ADCs and DACs*********************************/#define ADCPin 28                                                     // The µC ADC pinMux ADCMux(Pin(ADCPin, INPUT, PinType::Analog), Pinset(21, 20, 17));  // For addressing ADC channels with a CD4051 multiplexerAdafruit_MCP4725 DAC[numOutputChannels];                                                        // All DACsconst byte DACAddress[numOutputChannels] = { 0x60, 0x61, 0x62, 0x63, 0x60, 0x61, 0x62, 0x63 };  // Their respective (duplicate) i2c addressesconst byte DACi2cBus[numOutputChannels] = { 0, 0, 0, 0, 1, 1, 1, 1 };                           // Which bus each DAC is on respectivelybyte servoPin[numOutputChannels] = { 2, 3, 4, 5, 6, 7, 8, 9 };  // Servo pins mirror the output of their associated DAC channelServo servoChannel[numOutputChannels];                          // All output servos/********************************************************************Serial buffer and the values to parse between Easel and Palette & back********************************************************************/int DACSamplesFromEasel[numValuesFromEaselPerPacket] = { 511, 1023, 1535, 2047, 2559, 3071, 3583, 4095 };  // The buffer of values received from the Easelint ADCSamplesToEasel[numValuesToEaselPerPacket];                                                          // The values to send to the Easel/****************************************************************************************************************************************Main code****************************************************************************************************************************************/void setup() {  analogReadResolution(ADCBitDepth);  // Use the full bit depth of the ADCs  pinMode(LED_BUILTIN, OUTPUT);  pinMode(ADCPin, INPUT);  testBlink();  // Switch whenever something happens  // TinyUSBDevice.begin(115200);  // Baud setting is ignored, as it's handled by USB peripherals & Easel/OS  testBlink();                  // Switch whenever something happens  Wire.setClock(400000);  Wire.setSDA(0);  // Data line for DACs 0-3  Wire.setSCL(1);  // Serial clock line for DACs 0-3  testBlink();     // Switch whenever something happens  Wire1.setClock(1000000);  Wire1.setSDA(10);  // Data line for DACs 4-7  Wire1.setSCL(11);  // Serial clock line for DACs 4-7  testBlink();       // Switch whenever something happens  // begin all DACs on both i2c busses  for (byte thisDAC = 0; thisDAC < numOutputChannels; thisDAC++) {    if (DACi2cBus[thisDAC] == 0) {      DAC[thisDAC].begin(DACAddress[thisDAC]);          // begin each DAC on i2c bus 0    } else {                                            //      DAC[thisDAC].begin(DACAddress[thisDAC], &Wire1);  // begin each DAC on i2c bus 1    }  }  testBlink();  for (byte servoToSetup = 0; servoToSetup < numOutputChannels; servoToSetup++) {  // Make a servo object for each DAC channel    servoChannel[servoToSetup].attach(servoPin[servoToSetup]);                     // Attach each servo to its pin  }  testBlink();  // Switch whenever something happens  while (!findEasel())  // Look for the Easel on boot until the Palette gets back real values    ;}/**************************************** VVV LOOP VVV ****************************************/void loop() {  lastTime = micros() - lastTime;  // Time for the whole loop. Use for diagnostics if you gotta.  // Receive Serial values into array  receiveDACs();  testBlink();  // Read ADC values into array  readAllADCs();  // Send ADC values  sendADCToEasel();  // Update DAC values  updateAllDACs();  // Update all servo DAC values  updateAllServos();}/**************************************** ΛΛΛ LOOP ΛΛΛ ****************************************//******************************************************************************************Functions******************************************************************************************//*************************** DAC/output functions ***************************/// Receive all DAC values from the Easelvoid receiveDACs() {  for (byte thisDACChannel = 0; thisDACChannel < numOutputChannels; thisDACChannel++) {    DACSamplesFromEasel[thisDACChannel] = Serial.parseInt();  }}// Scroll through all DACs to update them with data from the Easelvoid updateAllDACs() {  for (byte thisi2cDACChannel = 0; thisi2cDACChannel < numOutputChannels; thisi2cDACChannel++) {  // Write to each DAC    DAC[thisi2cDACChannel].setVoltage(DACSamplesFromEasel[thisi2cDACChannel], false, 400000);  }}// Map analog values to servo outputsvoid updateAllServos() {  for (byte thisServoToUpdate = 0; thisServoToUpdate < numOutputChannels; thisServoToUpdate++) {    servoChannel[thisServoToUpdate].writeMicroseconds(map(DACSamplesFromEasel[thisServoToUpdate], 0, maxDACValue, 2000, 1000));  // Map the DAC value to the servos within their range  }}/*************************** ADC/input functions ***************************/void readAllADCs() {  for (byte thisADCChannel = 0; thisADCChannel < numInputChannels; thisADCChannel++) {    ADCSamplesToEasel[thisADCChannel] = ADCMux.read(thisADCChannel);  // use regular ol' analogRead() max sample rate: ~ 177Hz    // ADCSamplesToEasel[thisADCChannel] = (thisADCChannel * 512) - 1; // Test signal bypassing analogRead()    // if (!adc_fifo_is_empty()) { // Look at the ADC register, and if it's got anything in it, read it    //   ADCSamplesToEasel[thisADCChannel] = adc_fifo_get(); //    // }  }}void sendADCToEasel() {  for (byte thisADCChannel = 0; thisADCChannel < numInputChannels; thisADCChannel++) {    Serial.print(ADCSamplesToEasel[thisADCChannel]);    Serial.write(32);  // Space to delineate values    Serial.flush();  }  Serial.write(13);  // Carriage return to end the transmission}// Insert this function before sending to the Easel to just repeat back input channels so we can make sure the Easel is sending/Palette is receiving.void testRepeater() {  for (int thisChannelToRepeat = 1; thisChannelToRepeat < numInputChannels; thisChannelToRepeat++) {    ADCSamplesToEasel[thisChannelToRepeat] = DACSamplesFromEasel[thisChannelToRepeat];  }  ADCSamplesToEasel[0] = lastTime / 100;}// Call out for the Easel and blink the onboard LED if it's not found yetbool findEasel() {  if (Serial.available()) {  //If the Palette is receiving data from the Easel, start talking!    testBlink();    communicationTimeoutStart = micros();    return (1);  } else {  // When the Palette hasn't heard from the Easel yet, ping out with identifying information    sprintf(specsheet, "%s %s %u %u %u %u", modelName, firmwareVersion, numOutputChannels, DACBitDepth, numInputChannels, ADCBitDepth);    Serial.println(specsheet);    // Error code/waiting blinky for not hearing back from the Easel is ._ repeating    digitalWrite(LED_BUILTIN, LOW);    delay(100);    digitalWrite(LED_BUILTIN, HIGH);    delay(100);    digitalWrite(LED_BUILTIN, LOW);    delay(100);    digitalWrite(LED_BUILTIN, HIGH);    delay(200);    return (0);  }}void testBlink() {  digitalWrite(LED_BUILTIN, !ledState);  // Switch whenever something happens  ledState = !ledState;}
You must be logged in to vote

Replies: 1 comment

Comment options

voidreceiveDACs() {for (byte thisDACChannel =0; thisDACChannel < numOutputChannels; thisDACChannel++) {    DACSamplesFromEasel[thisDACChannel] = Serial.parseInt();  }}

Are you sure you want this? The behavior ofSerial.parseInt();,as documented, is:

Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read;

I would never ever ever use this function in my own sketch. Waiting for a timeout or an invalid character to arrive seems very fragile to me. What's the input data format? Are they deliminited with something? I would write everything non-blocking, just checking if there are enough bytes received in the serial buffer to start parsing the values in one go.

You must be logged in to vote
0 replies
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
General
Labels
None yet
2 participants
@JoshuaACNewman@maxgerhardt

[8]ページ先頭

©2009-2025 Movatter.jp