I am trying to send a sequence of commands from a PC to a Mega. The Mega in turn controls 2 signal generators (AD9833's).

The sequences can vary in length but always have the same format:

  1. A start byte of 255
  2. The length of the sequence
  3. Elements of the sequence: Channel 0: MSB, LSB & waveform, Channel 1: MSB, LSB & waveform

The PC side code:

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:
        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)
sendSequence([[1000, 0], [1000, 0], [2000, 0], [1000, 0], [2000, 0], [2000, 0]])

On the Arduino side:

#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

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

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;
          case 1:
            wave_seq_ch_0[i] = TRIANGLE;
          case 2:
            wave_seq_ch_0[i] = SQUARE;

        // 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;
          case 1:
            wave_seq_ch_1[i] = TRIANGLE;
          case 2:
            wave_seq_ch_1[i] = SQUARE;

      // 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);

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

// 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.
  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

My problem is that this does NOTHING. No matter what sequence I send the output of the signal generators never deviates from the set up defaults.

The generators do work & I have managed to control them via serial before but in that case I knew exactly how much information would be in the serial buffer before I read it in but in this case the sequence length isn't known a priori.

Is this a serial issue?

  • 1
    Are you waiting after opening the serial port in Python for the Arduino to reboot and run pass the bootloader before sending data? – Majenko Dec 17 '20 at 17:00
  • @Majenko I have added sleep(1.0) to the Python code after opening up the serial comms but I still get nothing – DrBwts Dec 17 '20 at 18:01
  • OK upped that to 10 seconds & hey presto! it worked! – DrBwts Dec 17 '20 at 18:05

The main issue here is, that you are reading data, that might not be received yet. This:

if (Serial.available()) {

will check if there is any data in the Serial buffer. As the code runs rather fast and communication over Serial at 9600 baud is relatively slow, this very well might be true, when not your complete message was received. So you might have received the start byte, but the rest of the data is still being transmitted. In that case Serial.read() will return -1.

With Serial (UART) the transmission happens with individual bytes, not in big chucks. You need to only read data, that is really received.

As you don't know the sequence length beforehand I would suggest 2 different ways:

  1. You can wait for 2 bytes being in the Serial buffer (start byte and length) and then again wait, until enough further bytes are in the Serial buffer (as you now have the length of the sequence).

  2. You can rebuild your code, so that it just places the data in an extra buffer, until you have received enough data (which you need to check via the received length byte). Then, when you have received the full message, you process it and then clear the buffer, waiting for a new message.

  • OK so I've added if (Serial.available > 1) then added while (Serial.available < seq_length*3){} but its still doing nothing. – DrBwts Dec 17 '20 at 17:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.