Pages: [1]   Go Down
Author Topic: Req: Controlling Nexus B08M04N 4 Digit LED display module via SPI - Help please  (Read 1438 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi!

I've done bit of PIC programming previously, but this is my first foray into Arduino, and first forum posting, so please be gentle with me!  smiley-confuse

I've got an old Nexus B08MO4N 4x 7-segment LED display, and I'm trying to drive it using the Arduino Uno and SPI library, from the Arduino 1.0 IDE running on a Windows XP laptop.

The data sheet for the Nexus display is here:
http://paginas.fe.up.pt/~hmiranda/st2/serial_display.pdf

The Nexus display requires a single clock (typically 500 Khz) and single data inputs. No data outputs.
The Chip Enable is wired to ground ("enable").

The data protocol is:
MSB first
Triggered on rising edge of clock
Clock is idle when low

36 bits of data as follows:
Bit 0 when set to "1" is the start bit
The subsequent 8 bits (1-8) define whether segments a-g and the decimal point are on or off for Digit 1 (Right hand position)
The next 24  bits (9-32) are similar for Digits 2-4
Bits 33 & 34 light additional LEDs if set to "1"
Bit 36 when set to "0" is the Null ("end of data") bit.

I've had a go at using SPI library to send a simple test message "1234" to the display.

I've only used the "MOSI" fuctions of the SPI library, since the Nexus only has inputs: there is no returning "SOMI" signal from the display to the Arduino.

The code compiles OK, but the display remains blank when I run the sketch
(It's not a hardware issue - the display works fine when not connected to the Arduino!)

I'm sure I've made some basic error in my use of the SPI library, or with the Processing language, and would be very grateful for your advice & help

Many thanks in anticipation

Dave
(UK)

(Sketch attached)

* Nexus_4LED_Driver_Test.ino (1.72 KB - downloaded 3 times.)
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I've only used the "MOSI" fuctions of the SPI library, since the Nexus only has inputs: there is no returning "SOMI" signal from the display to the Arduino.

It's called MISO (master in, slave out).

Try here:

http://www.gammon.com.au/forum/?id=11524

I was driving a 4 x 7 segment LED with SPI.

Quote
The code compiles OK, but the display remains blank when I run the sketch

Perhaps show your code?
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I've only used the "MOSI" fuctions of the SPI library, since the Nexus only has inputs

You can't "only use MOSI". Each SPI transfer transfers data two ways. You can ignore the return value if you want to.

http://www.gammon.com.au/spi
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Nck

Thanks for your reply.

>>It's called MISO (master in, slave out)
Sorry. Blame dyslexia, or confusion with Japanese soup... smiley-wink

>>Perhaps show your code?
Apologies. I thought I'd attached the code to my previous posting - I've cut and pasted the listing below.

Thanks for the link and your elegant solution to a similar problem.

My problem is more specific.
I want to send a 36 bit "word" of data along with a clock signal to a specific serial 4 digit LED display device; only updating intermittently at non-regular intervals (the Nexus module doesn't care about receiving regular updates; it just continues to display the efects of the last received packet of data until the next one arrives).

With a bit of time and effort I reckon I could write a bespoke routine specifically to toggle a clock signal at one output pin of the Arduino, and send the data from a second pin; but on looking through the Arduino web sites, I came across the SPI library, and figured that this might be a ready-made and more elegant way of sending clock and data signals in one direction.
The exercise would also be a useful introduction into understanding how to use the SPI library for more complex applications in future (e.g. 2-way communication with an EEPROM).

I've seen fairly complex examples of how to use the SPI commands to achieve 2-way communication with other devices like EPROMS and memory cards; however as I am only planning to send data in one direction (from Arduino to LED display), I need to know what elements of the SPI instructions I can ignore as "surplus to requirements" to keep my code neat, but still have the correct instructions for 1-way transmission of data.  

I'd be very grateful for your advice on my sketch and correction of errors.

Hope this makes sense - please let me know if you need further information or explanation.

Many thanks

Dave

(sketch pasted below)
======================================================================================


Code:
#include <SPI.h>

// Title: Nexus B08M04N Synchronous Serial 4 Digit LED module data test
// Author: Dave Williams 5 May 2012
// Action: To send "1234" to 4 segment LED display

// Nexus Display Pin Connections: 3 = Enable (0v);4 = Data;5 = Clock; 6 = 5V; 8 = Gnd (0v)
// 500kHz max data transfer rate

// Define Arduino output pins
#define DATAOUT 11   //Master Out-Slave In (MOSI) from Arduino pin 11 to Nexus Data In pin 4
#define SPICLOCK  13// Serial Clock (sck) from Arduino pin 13 to Nexus pin 5
 
// Define LED segments a-g & decimal point to be lit by hex codes corresponding to decimal numbers 0 - 9 :
// LOOKUP Digit[0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xF6]

// set up data buffer to hold 6 bytes of data representing start code, 4 digit codes, and finish code
char buffer [6];

void fill_buffer()
{
 buffer [0] = 0x01; //start bit  "1" to indicate start of data - i.e.
 buffer [1] = 0x66; // 1st (RH) digit = "4"
 buffer [2] = 0xF2; // 2nd digit = "3"
 buffer [3] = 0xDA; // 3rd digit = "2"
 buffer [4] = 0x60; // 4th (LH) digit = "1"
 buffer [5] = 0x00; // 0 = LED1 off, 0 = LED2 off, then 0 to indicate end of data - i.e. 000xxxxx
}

void setup()
{
  Serial.begin(9600); // Initiate Serial data transmission

  pinMode(DATAOUT, OUTPUT);   // Define Arduino MOSI & SCK pins as Outputs
  pinMode(SPICLOCK,OUTPUT);

  SPCR = 01010011;
  // interrupt disabled,spi enabled,msb first,Arduino = master,clk low when idle,
  // sample on leading edge of clk,system clock rate "3" (slowest; 250KHz)
}

void loop()
{
// Clear SPI status and data registers to remove any spurious data from previous runs
  SPSR = 0;
  SPDR = 0;
  delay(10);

// call routine to fill buffer with data
fill_buffer();

// send all 6 data bytes in sequence to Nexus LED display, MSB first; with clock signal
  for (int I=0;I=5;I++)
  {
    SPI.transfer(buffer[I]);
  }
delay(500);
}


Moderator edit: [code] ... [/code] tags added. (Nick Gammon)
« Last Edit: May 09, 2012, 04:27:06 am by Nick Gammon » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi

Still no joy getting the SPI library to send synchronous serial data to the Nexus LEDs

Can anyone please tell me what is wrong with my sketch, and how to achieve the result I want using the SPI library?

I've also tried sending data via a "brute force" technique of directly toggling the output pins of the Arduino to generate the clock and data signal (listing pasted below).
I estimate that the output frequency that I'm achieving with this method is a little lower than the 500kHz max speed of the Nexus; but I'm not sure how to calculate how long it takes the Arduino to perform each of the instructions (any tips?).

This approach doesn't seem to work either - no response from the LEDs.
?am I trying to toggle the output pins of the Arduino faster than they can respond?

Again, for my education, please explain where I've gone wrong with this sketch.

Many thanks

Dave

===============================================================

Code:
// Title: Nexus B08M04N Synchronous Serial 4 Digit LED module data test
// Author: Dave Williams 5 May 2012
// Action: To send "1234" to 4 segment LED display - "Brute force" bitwise method!

// Nexus Display Pin Connections: 3 = Enable (0v);4 = Data;5 = Clock; 6 = 5V; 8 = Gnd (0v)
// 500kHz max data transfer rate


void setup()
{
// Define Arduino output pins
#define DATA 10   //Data from Arduino pin 10 to Nexus Data In pin 4
#define CLOCK 11 // Serial Clock from Arduino pin 11 to Nexus pin 5
 
// Define LED segments a-g + decimal point to be lit by hex codes corresponding to decimal numbers 0 - 9 :
// LOOKUP Digit[0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xF6]

pinMode(DATA, OUTPUT);   // Define Clock & Data pins as digital Outputs
pinMode(CLOCK, OUTPUT);

int x = 0;
}

void loop()
{
//fill buffer with data
int buffer[4] = {0x66,0xF2,0xDA,0x60};

//buffer[0] = 0x66; // 1st (RH) digit = "4"
//buffer[1] = 0xF2; // 2nd digit = "3"
//buffer[2] = 0xDA; // 3rd digit = "2"
//buffer[3] = 0x60; // 4th (LH) digit = "1"

// send "START" bit ("1") down the DATA line with a clock pulse down the CLOCK line
  digitalWrite(DATA,HIGH);  
  digitalWrite (CLOCK,HIGH); //start clock pulse
  delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
  digitalWrite(CLOCK,LOW);//end clock pulse

// send all 4 data bytes in sequence to Nexus LED display, MSB first
  
  for (int I=0;I=3;I++){ // Look at each data byte in turn from byte 0 to 3
  int (x) = buffer[I];
  for (int y=7;y=0;y--){ // test bits in data from MSB (7) to LSB (0)
  
  
  if (bitRead(x,y) == 1)
  {
  digitalWrite(DATA,HIGH); //If data bit 'y' set, then send a data pulse with clock pulse
  }
  else
  {
  digitalWrite(DATA,LOW);  //If data bit 'y' clear, then send no data pulse with clock pulse
  }
  digitalWrite (CLOCK,HIGH); //start clock pulse
  delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
  digitalWrite(CLOCK,LOW);//end clock pulse
  
  } //loop to test next bit
  } //loop to test next byte
  
// Send 2 LED bits & STOP bit "0 0 0"
for (int I=0;I=2;I++){
  digitalWrite(DATA,LOW);  
  digitalWrite (CLOCK,HIGH); //start clock pulse
  delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
  digitalWrite(CLOCK,LOW);//end clock pulse
}

delay(1000);
}


Moderator edit: [code] ... [/code] tags added. (Nick Gammon)
« Last Edit: May 09, 2012, 04:26:06 am by Nick Gammon » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I want to send a 36 bit "word" of data ...

So, 5 bytes.

Quote
I need to know what elements of the SPI instructions I can ignore as "surplus to requirements" to keep my code neat, but still have the correct instructions for 1-way transmission of data. 

Did you read the page I linked? Each SPI.transfer sends and receives one byte. You can ignore the received byte if you don't care about it.

So, nothing is surplus to requirements.

Quote
Code:
  SPCR = 01010011;

Don't muck around, just do:

Code:
SPI.begin ();

Quote
Code:
// Clear SPI status and data registers to remove any spurious data from previous runs
  SPSR = 0;
  SPDR = 0;

Why? This is clearing the SPI status register.

Quote
Code:
// call routine to fill buffer with data
fill_buffer();

// send all 6 data bytes in sequence to Nexus LED display, MSB first; with clock signal
  for (int I=0;I=5;I++)
  {
    SPI.transfer(buffer[I]);
  }

Why call fill_buffer every time? It never changes.

Quote
Code:
  pinMode(DATAOUT, OUTPUT);   // Define Arduino MOSI & SCK pins as Outputs
  pinMode(SPICLOCK,OUTPUT);

You don't need to do that. SPI.begin() does it.

I notice you never set the slave select line low. That is probably the real problem.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Nick

>>Did you read the page I linked?
Yes! - honest!
I was just hoping that some of the code may be superfluous for 1-way transmission of data to such a simple device.
I will re-read your page though, and keep trying to implement the SPI solution in light of the comments from your last posting; more as a learning exercise for myself as anything else...

In the mean time, I've managed to get the "brute force" technique to work - I'd made an error when defining the "For" loops (I've used Python previously, and am still getting used to the differences in syntax between Python & C / Programming).
With these corrected as below, the LED display responds appropriately.

Thanks again for your help

Best wishes

Dave

===================================================
:
:
  for (int I=0;I<=3;I++){ // Look at each data byte in turn from byte 0 to 3
  int (x) = buffer;
  for (int y=7;y>=0;y--){ // test bits in data from MSB (7) to LSB (0)
:
:
// Send 2 LED bits & STOP bit "0 0 0"
for (int I=0;I<=2;I++)
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi

Have now got my Nexus LED display driver up and running, and can now use it to display the output from my other projects... smiley-wink

I've added the option to display or blank leading zeros.

My code works fine when the "blank leading zeros" option is selected.

The only glitch is that when operating in "display leading zeros" mode, the output from the display is sometimes unstable, with random LED segments illuminating until the Arduino "reset" button is pressed.
I think this is a hardware rather than coding problem: maybe because illuminating 4 digits worth of 7-segment displays showing 3 leading zeros (= 3 digits showing "0", each using 6 segments per "0" at around 20mA/segment) is drawing too much current from the USB cable to keep the Arduino ticking over reliably....

I'm attaching my code in case it is useful for anyone else.

I'm just getting started with Arduino, so I'm well aware that my sketch is not the most elegant or well written solution. I'd welcome any constructive criticism which would help me improve this routine in particular, and my coding skills in general.

Thanks in anticipation
Best wishes

Dave


Code:
// Title: Nexus B08M04N Synchronous Serial 4 Digit LED module data test
// Author: Dave Williams 12 May 2012
// Function: To send decimal value in range 0-9999 to to 4 segment LED display - "Brute force" bitwise method!

// Nexus Display Pin Connections: 3 = Enable (0v);4 = Data;5 = Clock; 6 = 5V; 8 = Gnd (0v)
// 500kHz max data transfer rate


void setup()
{
// Define Arduino output pins
#define DATA 10   //Data from Arduino pin 10 to Nexus Data In pin 4
#define CLOCK 11 // Serial Clock from Arduino pin 11 to Nexus pin 5

pinMode(DATA, OUTPUT);   // Define Clock & Data pins as digital Outputs
pinMode(CLOCK, OUTPUT);
}


void loop(){
//Test loop: Count repeatedly from 0-9999
for (int digits=0;digits<=9999;digits++){
int blanking = 1;//1 = blank leading zeros; 0 = don't blank leading zeroes
int k;
k = LEDsend(digits, blanking);
delay(50);
}
}

//Convert value of "digits" variable to serial code & send to Nexus
int LEDsend(int digits, int blanking){
digits = constrain(digits,0,9999);

//Declare function variables
int buffer[4]; //declare array for 7-segment display codes
//Lookup array corresponds to LED segments a-g & decimal point to illuminate to represent digits 0 - 9
int Lookup[]={252,96,218,242,102,182,190,224,254,246};

//Extract 1000s(buffer[3]), 100s(buffer[2]), 10s(buffer(1), and units(buffer[0])
//and look up code corresponding to which segments of 7 segment display to illuminate
buffer[3] = Lookup[(digits/1000)];
digits %= 1000;
buffer[2] = Lookup[(digits/100)];
digits %= 100;
buffer[1] = Lookup[(digits/10)];
buffer[0] = Lookup[(digits%10)];
 
//Blank leading zeros (252 == decimal code for "zero" on LEDs)
//only if "Blanking" parameter is Non-Zero (i.e. Boolean True)
if (blanking == true){
if (buffer[3] == 252){
buffer[3] = 0;
if (buffer[2] == 252){
buffer[2] = 0;
if (buffer[1] == 252){
buffer[1] = 0;
}}}}

//Send Data
delayMicroseconds(100);//wait before sending next packet of data to allow Nexus time to respond

// send "START" bit "1"
digitalWrite(DATA,HIGH); 
digitalWrite (CLOCK,HIGH); //start clock pulse
delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
digitalWrite(CLOCK,LOW);//end clock pulse

// send all 4 data bytes in sequence to Nexus LED display, Least Sig Digit (units, buffer(0)) first 
for(int I = 0;I <=3; I++){ // Look at each data byte in turn from byte 0 to 3
int(x) = buffer[I];

for(int y = 7;y >=0; y--){ // test bits in data from MSB (7) to LSB (0)
 
if (bitRead(x,y) == 1)
{
digitalWrite(DATA,HIGH); //If data bit 'y' set, then send a data pulse with clock pulse
}
else
{
digitalWrite(DATA,LOW);  //If data bit 'y' clear, then send no data pulse with clock pulse
}
digitalWrite (CLOCK,HIGH); //start clock pulse
delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
digitalWrite(CLOCK,LOW);//end clock pulse
} //loop to test next bit
} //loop to test next byte
 
// Send 2 LED bits & STOP bit "0 0 0"
for (int I=0;I<=2;I++){
  digitalWrite(DATA,LOW); 
  digitalWrite (CLOCK,HIGH); //start clock pulse
  delayMicroseconds(2);  //500KHz = 1 clock pulse every 2 microseconds
  digitalWrite(CLOCK,LOW);//end clock pulse 
}
}
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I can't see anything wrong with the code, apart from being slightly messy. You can always use the "reformat" tool in the IDE.

My guess is that with leading zeros displayed you are consuming too much current, hence the processor goes into "random behaviour" mode.  Look at whether your power supply can cope with all (most) segments being lit up.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Nick

A check with a multimeter showed that with all leading zeros lit, the board was consuming >200mA, which was causing erratic behaviour when the Arduino was powered from the USB cable alone.
Now it's fed from a 9V battery via a 7805 5V regulator IC, the circuit is behaving itself nicely  smiley-wink.

Thanks for the tip re: Tools>Autoformat

I've re-written the code using Arduino's built-in shiftOut() function (which I've just discovered!); and defined the variables for the "start" and "finish" commands ( = buffer[0] and buffer[5]) as global variables in order to avoid repeatedly re-assigning them with every call to the function.

Everything's looking a bit neater now, but as always I'd be grateful for any further comments/suggestions which will help me improve my coding skills and make things even more efficient and compact.

Best wishes

Dave


Code:
// Title: Nexus B08M04N Synchronous Serial 4 Digit LED module data test v2
// Author: Dave Williams 5 June 2012
// Function: To send decimal value in range 0-9999 to Nexus 4 digit 7 segment LED display - using shiftOut() command

// Nexus Display Pin Connections: 3 = Enable (0v);4 = Data;5 = Clock; 6 = 5V; 8 = Gnd (0v)
// 500kHz max data transfer rate

//Declare & assign global variables
int buffer[6] = {
  1,0,0,0,0,0}; //declare 6 byte array for 7-segment display start, data and stop codes
//Byte [0] = xxxxxxx1 = start bit (tells Nexus to start receiving data)
//Byte [5] = 000xxxxx = 'end of data' command to Nexus (2 zeros for additional LEDs, and 0 for stop bit)

int Lookup[]={
  252,96,218,242,102,182,190,224,254,246};
//Lookup array corresponds to LED segments a-g & decimal point to illuminate to represent digits 0 - 9

void setup()
{
  // Define Arduino output pins
#define DATA 10   //Data from Arduino pin 10 to Nexus Data In pin 4
#define CLOCK 11 // Serial Clock from Arduino pin 11 to Nexus Clock pin 5

  pinMode(DATA,OUTPUT);   //Define Clock & Data pins as digital Outputs
  pinMode(CLOCK,OUTPUT);
}

void loop(){
  //Test loop: Count repeatedly from 0-9999
  for (int digits=0;digits<=9999;digits++){
    int blanking = 1;//1 = blank leading zeros; 0 = don't blank leading zeros
    int k = LEDsend(digits, blanking);
    delay(50);//Delay to allow Nexus time to respond, and user to see digits change
  }
}

//Convert value of "digits" variable to serial code & send to Nexus
int LEDsend(int digits, int blanking){
  digits = constrain(digits,0,9999);

  //Extract 1000s (buffer[4]), 100s (buffer[3]), 10s (buffer[2]), and units (buffer[1])
  //and look up code corresponding to which segments of 7 segment display to illuminate
  buffer[4] = Lookup[(digits/1000)];//1000s
  digits %= 1000;
  buffer[3] = Lookup[(digits/100)];//100s
  digits %= 100;
  buffer[2] = Lookup[(digits/10)];//10s
  buffer[1] = Lookup[(digits%10)];//units

  //Blank leading zeros (252 == decimal code for "zero" on LEDs)
  //only if "Blanking" parameter is Non-Zero (i.e. Boolean True)
  if (blanking == true){
    if (buffer[4] == 252){
      buffer[4] = 0; //blank 1000s digit
      if (buffer[3] == 252){
        buffer[3] = 0; //blank 100s digit
        if (buffer[2] == 252){
          buffer[2] = 0; //blank 10s digit
        }
      }
    }
  }

  //Send Data
  // send all 6 start,data, and finish bytes in sequence to Nexus LED display; Least Sig Digit (units, buffer(1)) first 
  for(int I = 0;I <=5; I++){ // Look at each data byte in turn from byte 0 to 5
    int(x) = buffer[I];
    shiftOut(DATA,CLOCK,MSBFIRST,x);//send byte x, MSB first, with clock pulses
  } //loop to test next byte
}


Logged

Pages: [1]   Go Up
Jump to: