Hello Arduino hivemind. I'm still struggling with getting decent speed sending serial data between my Rasberry Pi and Arduino.
To recap:
I'm driving an Arduino Mega from a Raspberry Pi. The mega runs code from Adafruit to drive an LED Matrix. I want to pump 'screens' of pixel data from the Pi over the serial link to the mega to display. Each 'screen' is 32x32 RGB pixels.
Pixels are encoded as 4 bits per colour, so 12 bits make up one pixel. I can therefore send 2 pixels P1 and P2 in 3 bytes as: R1G1, B1R2, G2B2.
The matrix is 32x32 = 1024 pixels. Each pixels is 1.5 bytes, so a whole frame takes 1536 bytes. At 115200 baud using 8N1 I should get something like 92160bits to play with (80% of 115200) or 11520 bytes/s or 11.5 KB/s.
With a rate of 11.5KB/s I reckon I should be able to send 8 or 9 frames per second.
So... I tried using Python on the Pi as the sending code but it was too slow (using things like arrays) so I've switched to C.
The test script below sends a control byte to choose "draw screen mode" then 1536 bytes of data to show a screen of red pixels , then repeats with a screen of green pixels , then blue. However I'm only seeing 1 or so FPS (with the delays in the sender code). If I remove the delays my output gets garbled - a mess of RGB pixels.
I first tried connecting the two boards over the USB serial link, but Grumpy_Mike mentioned the converter chips encapsulate my data in frames of their own so the data rate isn't maintained. So now I'm using the secondary UART on the Pi (/dev/ttyAMA0), and that is going direct into the Arduino Mega (Serial1). (I've commented this out in /etc/initab).
I've tried increasing the buffer on the arduino by changing the value in HardwareSerial.cpp but it makes no difference.
When I was connected via serial over USB I had the Arduino ACKing (by printing an "A") after receiving each byte and then waiting for the ACK in the sendData function before continuing. This worked but was also slow. 1 frame a sec or so.
I'm not sure where what else to try, other than using something like SPI instead.
Any thoughts appreciated...
Here is the C send code that runs on the Pi:
#include "rs232.h"
#include <sched.h>
#define COMPORT 0 // this is '/dev/ttyAMA0' (for raspberry pi UART pins)
#define BAUDRATE 115200 // or whatever baudrate you want
#define RECEIVE_CHARS 4096 // or whatever size of buffer you want to receive
#define SEND_CHARS 1 // or whatever size of buffer you want to send
//Set high Priority - Need to be root to do this.
void setHighPri (void)
{
struct sched_param sched ;
memset (&sched, 0, sizeof(sched)) ;
sched.sched_priority = 10 ;
if (sched_setscheduler (0, SCHED_RR, &sched))
printf ("Warning: Unable to set high priority\n") ;
}
void sendData(unsigned char send_byte){
SendByte(COMPORT, send_byte);
usleep(250);
}
void drawScreen(int r1, int g1, int b1){ //pass in rgb colours for all pixels - this is a test so dont need to set individual pixels
sendData(3); //3 = enter drawScreen mode
int x=0;
int y=0;
int r2,g2,b2 //pixels
int byt1,byt2,byt3; //3 bytes = 2 pixels
//2 pixels per 3 bytes. 1024 pixels = 1536 bytes to send
while (y < 32){
//r1=0 //Pixel 1 to send - these would normally be set individually via an array but passed in for this test
//g1=0
//b1=0
x++; //goto next pixel
r2 = r1; //Pixel 2 - again would normally set via array but - just set to same as 1st pixel for this test
g2 = g1;
b2 = b1;
x++; //goto next pixel
if (x==32){
x=0;
y=y+1;
}
//send 3 bytes now we have our 2 pixels, pack nibbles into byte
byt1 = 0xff & (((0xff & g1) << 4) | (0xff & r1)); //pack r in lhs g rhs
sendData(byt1);
byt2 = 0xff & (((0xff & r2) << 4) | (0xff & b1));
sendData(byt2);
byt3 = 0xff & (((0xff & b2) << 4) | (0xff & g2));
sendData(byt3);
}
}
int main (int argc, char **argv) {
setHighPri () ; //run as high priority - doesnt seem to make any odds
OpenComport(COMPORT, BAUDRATE);
sleep(2);
while(1) {
drawScreen(5,0,0); //draw screen of red
usleep(1000);
drawScreen(0,5,0); //draw screen of green
usleep(1000);
drawScreen(0,0,5); //draw screen of bue
usleep(1000);
}
CloseComport(COMPORT);
}
Here is the arduino receive code:
#include "RGBmatrixPanel.h"
#define A 5 //A3
#define B 4 //A2
#define C 3 //A1
#define D 2 //A0
#define CLK 10 // was 8 MUST be on PORTB!
#define LAT 9
#define OE 7
RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, true);
void setup() {
Serial1.begin(115200);
matrix.begin();
//Serial.println("matrix routine started");
}
void loop() {
byte incomingByte = 0;
if (Serial1.available() > 0) { //if > 1 get 1st byte which is control byte
// read the incoming byte:
incomingByte = Serial1.read();
switch (incomingByte) {
case 0:
//Serial.println("clear screen");
clearDisp();
break;
case 1:
//Serial.println("swap buffers");
matrix.swapBuffers(false);
break;
case 3:
//Serial.println("Drawing screen of 1024 pixels");
drawScreen();
break;
case 49:
Serial.println("light a test led");
testLed();
break;
//default:
//Serial.println("unknown cmd");
}
}
}
void drawScreen() {
//Receiving 32x32 pixels = 1024 pixels. Each pixel is 3x4 bit values, so each pixel = 1.5 bytes. Total bytes to receive is 1536
byte x = 0; //screen x
byte y = 0; //screen y
byte r = 0; //red
byte g = 0; //green
byte b = 0; //blue
byte data = 0; //incoming byte
//unsigned long startTime= millis();
while (y < 32 ) {
//wait for 1st byte of data
while (Serial1.available() < 1) {
//do nothing
}
//ack
//Serial.print("A");
//read 1st byte and split into 2 lots of 4 bits, (red and green)
data = byte (Serial1.read());
r = data & B00001111;
g = data >> 4;
//wait for 2nd byte
while (Serial1.available() < 1) {
//do nothing
}
//ack
//Serial.print("A");
//read 2nd byte - b and r of next pixel
data = byte (Serial1.read());
b = data & B00001111;
//draw pixel 1
matrix.drawPixel(x, y, matrix.Color444(r,g,b));
//inc x for next pixel along
x++;
//get red val from 2nd byte for 2nd pixel
r = data >> 4;
//wait for 3rd byte
while (Serial1.available() < 1) {
//do nothing
}
//ack
//Serial.print("A");
//read 3rd byte, g and b
data = byte (Serial1.read());
g = data & B00001111;
b = data >> 4;
//draw pixel 2
matrix.drawPixel(x, y, matrix.Color444(r,g,b));
//inc for next pixel or reset if x at end of row
if (x == 31){
x=0;
y++;
}
else {
x++;
}
}
//print time it took
//Serial.println(millis() - startTime);
//when all data received, swap buffers to show screen
matrix.swapBuffers(false);
}
void clearDisp() {
matrix.fill(matrix.Color333(0, 0, 0));
}
void testLed() {
matrix.drawPixel(0, 0, matrix.Color444(15,0,0));
matrix.swapBuffers(false);
delay(500);
matrix.drawPixel(0, 0, matrix.Color444(0,0,0));
matrix.swapBuffers(false);
}