Here is a new logic capture of exactly the same circuit using my new code that I just wrote. It's interrupt timer based and uses the usb_serial library to stream the data back to the computer at 62.5 kHz. That figure comes from the 16 MHz clock triggering a timer overflow every 256 clocks.
This capture came out absolutely perfect, compare it to the previous one - it's beautiful! show's how misleading a capture can be without enough samples. I also put an error detector into the code which should light up the LED on the board if a sample is missed from the usb_serial communication taking too much time - this should tell me if try running the capture with too many samples per second although I may need more error detection.
The first thing I did was run the usb_serial tx benchmark and then wrote my own code to send data - at the start it was getting 1/3'd the speed but that's because I was sending one character at a time rather than sending a reasonable sized buffers.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdint.h>
#include "usb_serial.h"
#define LED_CONFIG (DDRD |= (1<<6))
#define LED_ON (PORTD &= ~(1<<6))
#define LED_OFF (PORTD |= (1<<6))
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
volatile int n;
uint8_t buf[64];
int main(void) {
CPU_PRESCALE(0);
LED_CONFIG;
LED_OFF;
// set the F port to act as input pins without a pullup resistor
PORTF = 0;
DDRF = 0;
/* ******************* */
// at 16 MHz the fastest timer will overflow at 16/256 = 62.5 kHz
// Setup the registers for Normal mode:
// TCCR0A is [COMOA1,0,COM0B1,0,-,-,WGM01,0]
// compare output match bits are all zero
// waveform generation mode is zero to select Normal mode
TCCR0A = 0;
// TCCR0B is [FOC0A,B,-,-,WGM02,CS02,1,0]
// Force output compare is zero
// Clock select is 0,0,1 for the non-prescaled clock
TCCR0B = 1;
// Timer/counter interrupt mask register
// the TOIE0 bit enables interrupts when the timer overflows
TIMSK0 = (1<<TOIE0);
// Timer counter register is reset to zero
TCNT0 = 0;
/* ******************* */
buf[0] = 0xC0;
buf[1] = 0xB0;
n = 2;
// USB initialization routine taken from example.c
usb_init();
while (!usb_configured());
_delay_ms(1000);
// wait for the user to run their terminal emulator program
// which sets DTR to indicate it is ready to receive.
while (!(usb_serial_get_control() & USB_SERIAL_DTR)) /* wait */ ;
// discard anything that was received prior. Sometimes the
// operating system or other software will send a modem
// "AT command", which can still be buffered.
// enable global interrupts
sei();
while(1) {
if(n == 64) {
usb_serial_write(buf, 64);
usb_serial_flush_output();
n = 2;
}
}
}
ISR(TIMER0_OVF_vect) {
if(n <= 63) {
buf[n] = PINF;
n++;
}
else {
// looks like the main loop didn't manage to write in time and we missed a sample
LED_ON;
}
}
I used the following script on my computer to stream the data from the virtual serial device into a file (withour having to wait for line buffering):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#define BLOCKSIZE 1024
int main(int argc, char **argv)
{
char buf[BLOCKSIZE];
int port;
long n, m, sum=0, dotcount=0;
struct termios settings;
FILE *dump;
if (argc < 3) {
fprintf(stderr, "Usage: serial_dump <port> <filename>\n");
#if #system(linux)
fprintf(stderr, "Example: serial_dump /dev/ttyACM0 <filename>\n");
#else
fprintf(stderr, "Example: serial_dump /dev/cu.usbmodem12341 <filename>\n");
#endif
return 1;
}
// Open the output file
dump = fopen(argv[2], "w");
if(!dump) {
fprintf(stderr, "Unable to open %s for writing\n", argv[2]);
return 1;
}
// Open the serial port
port = open(argv[1], O_RDONLY);
if (port < 0) {
fprintf(stderr, "Unable to open %s for reading\n", argv[1]);
return 1;
}
// Configure the port
tcgetattr(port, &settings);
cfmakeraw(&settings);
tcsetattr(port, TCSANOW, &settings);
m = 0;
while (1) {
n = read(port, buf+m, sizeof(buf)-m);
m += n;
if (n < 1) {
fprintf(stderr, "error reading from %s\n", argv[1]);
break;
}
if (m == sizeof(buf)) {
fwrite(buf, sizeof(buf), 1, dump);
fflush(dump);
m = 0;
}
}
close(port);
return 0;
}
And... I just noticed that this throws away the last <BLOCKSIZE bytes of the data without writing them, better fix that for next time.. (although this is not a problem in the way I current use the program)and as the packets themselves include a "magic" (this may be considered superstitious and I might remove it in future) to detect if any bytes were lots I used the following to convert the dump into something I could open in sigrok:
#include <stdio.h>
void main(void) {
int i;
int b;
i = 0;
while((b = getchar()) != EOF) {
if(i == 0) {
if(b != 0xC0) {
fprintf(stderr, "ERR");
return;
}
}
else if(i == 1) {
if(b != 0xB0) {
fprintf(stderr, "ERR!");
return;
}
}
else {
putchar(b);
fflush(stdout);
}
i++;
if(i == 64) {
i = 0;
}
}
fflush(stdout);
}