Отправка последовательности переменной длины по последовательному каналу с ПК на Mega

Я пытаюсь отправить последовательность команд с ПК на Мега. Mega, в свою очередь, управляет 2 генераторами сигналов (AD9833).

Последовательности могут отличаться по длине, но всегда имеют один и тот же формат:

  1. Начальный байт 255
  2. Длина последовательности
  3. Элементы последовательности: Канал 0: MSB, LSB и форма волны, Канал 1: MSB, LSB и форма волны

Код на стороне ПК:

import serial
import struct

usbport    = 'COM3'
ser        = serial.Serial(usbport, 9600, timeout=1)


def sendSequence(seq):
    
    ''' Send a sequence of frequencies along with waveform '''
    
    # Start byte & sequence length
    ser.write(struct.pack('>B', 255))
    ser.write(struct.pack('>B', len(seq)))
    
    for s in seq:
        
        print(s)
        MSB, LSB = (s[0] & 0xFFFF).to_bytes(2, 'big')     # Split frequency to send
        ser.write(struct.pack('>B', MSB))
        ser.write(struct.pack('>B', LSB))
        ser.write(struct.pack('>B', s[1]))                # Waveform
    
        combined = ((MSB & 0xFF) << 8) | (LSB & 0xFF)
        print(combined)
    
sendSequence([[1000, 0], [1000, 0], [2000, 0], [1000, 0], [2000, 0], [2000, 0]])
ser.close()

На стороне Arduino:

#include <SPI.h>

// Set up Serial data variables
byte freq_MSB;
byte freq_LSB;
int startbyte;
unsigned long freq_seq_ch_0[16];
int wave_seq_ch_0[16];
unsigned long freq_seq_ch_1[16];
int wave_seq_ch_1[16];
int wave_select = 0;
int seq_length;
long f_2;
long f_1;

const int SINE     = 0x2000;                // Define AD9833's waveform register value.
const int SQUARE   = 0x2028;                // When we update the frequency, we need to
const int TRIANGLE = 0x2002;                // define the waveform when we end writing.    
int wave           = SINE;

const float refFreq = 25000000.0;           // On-board crystal reference frequency

const int FSYNCA = 53;                       // Standard SPI pins for the AD9833 waveform generator.
const int FSYNCB = 49;
const int CLK    = 52;                      

unsigned long pause     = 5000;
unsigned long freq      = 1000;              // Set initial frequency.
int i;


void setup() { 

  // Set slave ocntrol pins
  pinMode(FSYNCA, OUTPUT);                  
  pinMode(FSYNCB, OUTPUT);
  digitalWrite(FSYNCA, HIGH);
  digitalWrite(FSYNCB, HIGH);

  // Set up SPI
  SPI.begin();
  SPI.setDataMode(SPI_MODE2);
  delay(50); 

  // Set up AD9833s
  AD9833reset();                                   // Reset AD9833 module after power-up.
  delay(50);
  AD9833setFrequency(freq, SINE, FSYNCA);          // Set the frequency and Sine Wave output
  AD9833setFrequency(freq, SINE, FSYNCB);
  
  // Open the serial connection, 9600 baud
  Serial.begin(9600);
}



void loop() {

   // Wait for serial input 
  if (Serial.available()) {   
    
    startbyte = Serial.read();              // Read the first byte
    if (startbyte == 255) {
      seq_length = Serial.read();
      
      for (i = 0; i < seq_length; i++) {

        // Channel 0
        freq_MSB = Serial.read();              
        freq_LSB = Serial.read();
        f_2 = (long)freq_MSB << 8;
        f_1 = (long)freq_LSB; 
        freq_seq_ch_0[i] = f_2 | f_1;
        wave_select = Serial.read();
        switch (wave_select) {
          case 0:
            wave_seq_ch_0[i] = SINE;
            break;
          case 1:
            wave_seq_ch_0[i] = TRIANGLE;
            break;
          case 2:
            wave_seq_ch_0[i] = SQUARE;
            break;
          }

        // Channel 1
        freq_MSB = Serial.read();              
        freq_LSB = Serial.read();
        f_2 = (long)freq_MSB << 8;
        f_1 = (long)freq_LSB;
        freq_seq_ch_1[i] = f_2 | f_1; 
        wave_select = Serial.read();
        switch (wave_select) {
          case 0:
            wave_seq_ch_1[i] = SINE;
            break;
          case 1:
            wave_seq_ch_1[i] = TRIANGLE;
            break;
          case 2:
            wave_seq_ch_1[i] = SQUARE;
            break;
          }
      }

      // Send sequence to signal generators
      for (i = 0; i < seq_length; i++) {
        AD9833setFrequency(freq_seq_ch_0[i], wave_seq_ch_0[i], FSYNCA);
        AD9833setFrequency(freq_seq_ch_1[i], wave_seq_ch_1[i], FSYNCB);
        delay(3000);
      }
     
    }
  
  } 
}

// AD9833 documentation advises a 'Reset' on first applying power.
void AD9833reset() {
  WriteRegister(0x100, FSYNCA);   // Write '1' to AD9833 Control register bit D8.
  delay(10);
  WriteRegister(0x100, FSYNCB);
  delay(10);
}

// Set the frequency and waveform registers in the AD9833.
void AD9833setFrequency(long frequency, int Waveform, int channel) {

  long FreqWord = (frequency * pow(2, 28)) / refFreq;

  int MSB = (int)((FreqWord & 0xFFFC000) >> 14);    //Only lower 14 bits are used for data
  int LSB = (int)(FreqWord & 0x3FFF);
  
  //Set control bits 15 ande 14 to 0 and 1, respectively, for frequency register 0
  LSB |= 0x4000;
  MSB |= 0x4000; 
  
  WriteRegister(0x2100, channel);   
  WriteRegister(LSB, channel);                  // Write lower 16 bits to AD9833 registers
  WriteRegister(MSB, channel);                  // Write upper 16 bits to AD9833 registers.
  WriteRegister(0xC000, channel);               // Phase register
  WriteRegister(Waveform, channel);             // Exit & Reset to SINE, SQUARE or TRIANGLE

}

void WriteRegister(int dat, int channel) { 
  
  // Display and AD9833 use different SPI MODES so it has to be set for the AD9833 here.
  //SPI.setDataMode(SPI_MODE2);       
  
  digitalWrite(channel, LOW);           // Set FSYNC low before writing to AD9833 registers
  delayMicroseconds(10);              // Give AD9833 time to get ready to receive data.
  
  SPI.transfer(highByte(dat));        // Each AD9833 register is 32 bits wide and each 16
  SPI.transfer(lowByte(dat));         // bits has to be transferred as 2 x 8-bit bytes.

  digitalWrite(channel, HIGH);          //Write done. Set FSYNC high
}

Моя проблема в том, что это НИЧЕГО НЕ дает. Независимо от того, какую последовательность я отправляю, выходные данные генераторов сигналов никогда не отклоняются от заданных значений по умолчанию.

Генераторы действительно работают, и мне раньше удавалось управлять ими через последовательный, но в этом случае я точно знал, сколько информации будет в последовательном буфере, прежде чем я ее прочитаю, но в этом случае длина последовательности априори неизвестна.

Это серийная проблема?

, 👍-1

Обсуждение

Вы ждете после открытия последовательного порта в Python, чтобы Arduino перезагрузился и запустил загрузчик перед отправкой данных?, @Majenko

@Majenko Я добавил `sleep(1.0) " в код Python после открытия последовательной связи, но я все равно ничего не получаю, @DrBwts

ОК увеличил это до 10 секунд и эй, престо! это сработало!, @DrBwts


1 ответ


0

Основная проблема здесь в том, что вы читаете данные, которые, возможно, еще не получены. Это:

if (Serial.available()) {

проверит, есть ли какие-либо данные в последовательном буфере. Поскольку код выполняется довольно быстро, а связь по последовательному каналу со скоростью 9600 бод относительно медленная, это вполне может быть правдой, если ваше сообщение было получено не полностью. Таким образом, вы, возможно, получили начальный байт, но остальные данные все еще передаются. В этом случае Serial.read() вернет -1.

С последовательным (UART) передача происходит с отдельными байтами, а не большими порциями. Вам нужно только прочитать данные, которые действительно получены.

Поскольку вы заранее не знаете длину последовательности, я бы предложил 2 разных способа:

  1. Вы можете подождать, пока в последовательном буфере будет 2 байта (начальный байт и длина), а затем снова подождать, пока в последовательном буфере не будет достаточно дополнительных байтов (поскольку теперь у вас есть длина последовательности).

  2. Вы можете перестроить свой код так, чтобы он просто помещал данные в дополнительный буфер, пока вы не получите достаточно данных (которые вам нужно проверить с помощью полученного байта длины). Затем, когда вы получили полное сообщение, вы обрабатываете его, а затем очищаете буфер, ожидая нового сообщения.

,

Итак, я добавил " if (Serial.available > 1)", а затем добавил " while (Serial.available < seq_length*3){}` но он все еще ничего не делает., @DrBwts