Go Down

Topic: Need help porting a demo program from basic stamp (Read 5286 times) previous topic - next topic

Jona

Hi,

I have been working on my digital scoreboard project (see these threads for details: http://www.sparkfun.com/cgi-bin/phpbb/viewtopic.php?t=3897 and http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1154868522) for the last week or so. By following the suggestions posted on the Spark Fun forum, and using the SPI EEPROM tutorial found here: http://www.arduino.cc/en/Tutorial/SPIEEPROM I have been able to successfully connect the display unit and the Arduino. I was even able to see the display unit update itself when I uploaded the led_blink program to the board (although it wasn't displaying actual numbers.)

Now I have moved on to attempting to get the sample program that came with the display unit ported from Basic Stamp to Programming syntax. For the most part I have gotten it written, but there are a few sticking points that I just can't get past. Primarily the SHIFTOUT function in Basic doesn't seem to have a counterpart in Programming. The sample program I am working with is the first program listed in this spec sheet: http://user.pa.net/~bean/hc4led/HC4LED%20Datasheet.pdf

This is what the Basic reference says about the SHIFTOUT function.
Code: [Select]
SHIFTOUT Dpin, Cpin, Mode, [ OutputData { \Bits } { ,OutputData { \Bits }...} ]

Function  
Shift data out to a synchronous serial device.
? Dpin is a variable/constant/expression (0 - 15) that specifies the I/O
pin that will be connected to the synchronous serial device's data
input. This pin will be set to output mode.
? Cpin is a variable/constant/expression (0 - 15) that specifies the I/O
pin that will be connected to the synchronous serial device's clock
input. This pin will be set to output mode.
? Mode is a variable/constant/expression (0 - 1), or one of two
predefined symbols, that tells SHIFTOUT the order in which data
bits are to be arranged.  See Table 5.113 for value and symbol
definitions.
? OutputData is a variable/constant/expression containing the data to
be sent.  
? Bits is an optional variable/constant/expression (1 - 16) specifying
how many bits are to be output by SHIFTOUT. When the Bits
argument is given, the BASIC Stamp transmits the rightmost
number of bits specifed, regardless of the Mode. If no Bits argument
is given, SHIFTOUT defaults to 8 bits.

Explanation
SHIFTIN and SHIFTOUT provide an easy method of acquiring data from
synchronous serial devices.  Synchronous serial differs from asynchronous
serial (like SERIN and SEROUT) in that the timing of data bits (on a data
line) is specified in relationship to clock pulses (on a clock line). Data bits
may be valid after the rising or falling edge of the clock line. This kind of
serial protocol is called Synchronous Peripheral Interface (SPI) and is
commonly used by controller peripherals like ADCs, DACs, clocks,
memory devices, etc.  


Can someone help me in getting the Programming equivalent of the Basic SHIFTOUT funtion?

Jona

Okay, well it looks like many people are viewing this post, but no one has bothered to reply so I am going to reply to myself. I took a stab at writing a program based on the SPI tutorials found here: http://www.arduino.cc/en/Main/LearnArduino. The purpose of this program is to simply send a value to the display and then make it turn on. Each value is from the array variable and it coresponds to a segment in the 7-segment display. I am trying to imitate what the demo program that came with the display does, but mostly I just want to successfully write to the display so that I might understand better how this all works.

[glow]Any help would be greatly appreciated![/glow]

Here is my code:
Code: [Select]
#define DATAOUT 11
#define SPICLOCK  13

int bars[7] = {1,2,4,8,16,32,64};

char spi_transfer(volatile char data)
{
 // Start the transmission
 SPDR = data;

 // Wait the end of the transmission
 while (!(SPSR & (1<<SPIF)))
 {
 };

 // return the received byte
 return SPDR;
}

void setup()
{
 byte clr;
 pinMode(DATAOUT, OUTPUT);
 pinMode(SPICLOCK,OUTPUT);

 // SPCR = 01010000
 //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
 //sample on leading edge of clk,system clock/4 (fastest)
 SPCR = (1<<SPE)|(1<<MSTR);
 clr=SPSR;
 clr=SPDR;
 delay(10);
}

void loop()
{
 int i;
 for (i = 0; i < 7; i = i + 1) {
   Serial.println(i);
   spi_transfer(bars[i]);
   digitalWrite(SPICLOCK,HIGH);
   delay(1000);
 }
}


missmoun

You need to write your own shiftin function.
Got this code from Tom Igoe to work nicely for me. Might want to try it out:

void shiftOut(int dataPin, int clockPin, byte dataOut) {
   // This shifts bits out MSB first, on the rising edge of the clock:
   int i;     // bit counter

   // set the pinModes for the clock and data pins:
   pinMode(clockPin, OUTPUT);
   pinMode(dataPin, OUTPUT);
   // for debugging only: print out what you're going to shift
   Serial.print (dataOut, DEC);
   Serial.print("   ");

   // iterate over the bits, and shift them:
   for (i=7; i>=0; i--)  {
     // if this bit of the dataOut variable is a 1,
     // then set the data pin high to shift out a 1:
     if ( dataOut & (1<<i) ) {
       digitalWrite(dataPin, 1);
     }
     // if this bit of the dataOut variable is a 0,
     // then set the data pin low to shift out a 0:
     else {
       digitalWrite(dataPin, 0);
     }
     // pulse the clock:
     digitalWrite(clockPin, HIGH);
     delayMicroseconds(100);
     digitalWrite(clockPin, LOW);
   }
}


JJ

#3
Oct 07, 2006, 04:33 pm Last Edit: Oct 07, 2006, 04:47 pm by JJ Reason: 1
I was just about to reply to this post, I've been working with the HC4LED for a few hours now, and I was just sitting down to working on translating the basic stamp prog. I did get some output with the bit shift program from the example source, but I'll see what I can do with the code listed above.

JJ

#4
Oct 07, 2006, 08:00 pm Last Edit: Oct 07, 2006, 08:01 pm by JJ Reason: 1
So, here is my current program for the display. This will add one bit and remove one bit from alternating digits. I just wanted to post this in case this gave you the hint you needed. I havn't had enough time this morning to do anything else, but I thought you might be able to do something with this. I think the clock delay on the previous code might have been too quick to actually triger the display.

Code: [Select]

int clockPin = 9;
int dataPin = 8;
int ledPin = 13;

int dataCur = 0;
int count = 0;

void setup()
{
 Serial.begin(9600);
 pinMode(dataPin, OUTPUT);
 pinMode(clockPin, OUTPUT);  
 pinMode(ledPin, OUTPUT);      
}

void testOut() {
 int i;     // bit counter

 for (i=7; i>=0; i--)  {

   digitalWrite(dataPin, LOW);

   // pulse the clock:
   digitalWrite(clockPin, HIGH);
   delay(10);
   digitalWrite(clockPin, LOW);
   digitalWrite(ledPin, HIGH);
   delay(1000);
   digitalWrite(ledPin, LOW);
 }
 
 for (i=7; i>=0; i--)  {

   digitalWrite(dataPin, HIGH);

   // pulse the clock:
   digitalWrite(clockPin, HIGH);
   delay(10);
   digitalWrite(clockPin, LOW);
   digitalWrite(ledPin, HIGH);
   delay(1000);
   digitalWrite(ledPin, LOW);
 }
 
}

void loop()
{
 dataCur = 0;
 for (count = 0; count < 4; count++) {
   dataCur = message[count];
   //shiftOut(dataCur);
   testOut();
 }
 delay(1000);
}

Jona

Wow, freakin' awesome!

Major thanks to both mismoun and JJ.

I should be able to get time to try this out this evening, so hopefully I will have what I need based on your example code.

Again, thanks for the time and help.

I will repost when I have something new to add.

JJ

I've got a bit more now, I have a sample which scrolls numbers in series accross the screen, I can post this, but what I'd like to do is get one that will display any arbitrary 4 digit number.I'd eventually like to make a temperature / sensor logging board. Once I have this displaying arbitrary number I can start polling that for input.

Jona

Hey JJ,

I am trying to work with your code and I am getting a compile error because the array 'messages' doesn't seem to be declared anywhere.

Could you double-check what you posted against what you had working to see what is missing?

Also, I would be really interested in seeing what else you have put together so far.

Thanks,
Jona

Jona

So I tried just commenting out the line that references the 'messages' variable and it compiles. After uploading it to the board, I am seeing some activity on the display unit. It seems to be adding a single bar at a time and then removing a single bar at a time. This is pretty cool, but I don't see in the code where it is getting the value to display.

So, I guess I am now most curious about how to actually display a specific number on the display. It would be pretty cool to see it count up from 0 to 9999 and then back down again.

Have you made any progress on your attempts to display an arbitrary 4 digit number?

JJ

Here is my current code, which does just that. It accepts input from an analog input and writes that to the 4 digit display. If we could avoid using stdlib I bet this would probably shave quite a bit off the footprint. I'm surprised at the abundant use of pointers in stdlib, but it was good review. I'll open up a thread for my analog sensor in a little bit. I think I've put together something that generates a stable response. The analog sensor was all over the place until I put in an 10k(?) resistor and tied it to ground. Keeping an average also helps make the display readable. Another thing to do would be to poll the sensor frequently, but only calculate and display the average once every so often. Finally, once I have an analog circuit I like, I'll write some calibration for the thermistor.

//

#include <stdlib.h>

int clockPin = 9;
int dataPin = 8;
int ledPin = 13;
int thermPin = 2;
int refPin = 2;

int flashDelay = 1400;
int minDelay = 500;

int digits[10] = { 126, 24, 109, 61, 27, 55, 119, 28, 127, 31 }; // define numerals (0-9)
int digitC = 60;
int digitF = 71;
int digitE = 103;

int thermRaw; //raw thermistor data
int thermRawAvg[5] = {0,0,0,0,0};
int thermRawAvgI = 0; // interator for the average
int thermTemp; //calibrated thermistor temp (F)

char* numStr = "                        "; // allocate enough free space for our conversion

void setup()
{
 Serial.begin(9600);
 pinMode(dataPin, OUTPUT);
 pinMode(clockPin, OUTPUT);  
 pinMode(ledPin, OUTPUT);
 pinMode(refPin, OUTPUT);
   
 digitalWrite(refPin, HIGH);
}  

void numOut(int num){
 int g;
 
 char* *numStrPtr;
 //char *numDigitPtr;
 
 numStrPtr = &numStr;
 //numDigitPtr = &numDigit;
 
 itoa(num, *numStrPtr, 10);
 
 for (g=3; g >= 0; g--) {
       
   if (g == 0) {
     digitOut(digits[numStr[g]-48], flashDelay);
   } else {
     digitOut(digits[numStr[g]-48], minDelay);
   }

 }

}

void digitOut(byte dataOut, int curDelay) {
 // This shifts bits out MSB first, on the rising edge of the clock:
 int i;     // bit counter
 if (curDelay < minDelay) { curDelay = minDelay; } // delay  
   for (i=7; i>=0; i--)  {

     if ( dataOut & (1<<i) ) {
       digitalWrite(dataPin, HIGH);
     }
     
     // if this bit of the dataOut variable is a 0,
     // then set the data pin low to shift out a 0:
     else {
       digitalWrite(dataPin, LOW);
     }

     digitalWrite(clockPin, HIGH);
     // pulse the clock:
     if (i == 0) {
       delayMicroseconds(curDelay);
     } else {
       delayMicroseconds(minDelay);
     }
     digitalWrite(clockPin, LOW);

   }

}

void loop()
{
   int i;
   int temp = 0;

   thermRaw = analogRead(thermPin);
       
   if (thermRawAvgI == 5) { thermRawAvgI = 0; }
   
   thermRawAvg[thermRawAvgI] = thermRaw;
   
   for ( i=0; i < 5; i++) {
      temp += thermRawAvg;
   }
   
   numOut(temp / 5);
     
   delay(50);
   
   thermRawAvgI++;  

}

Jona

Hey JJ, pretty sweet sketch. Thanks for posting it.

I have successfully modified your sketch to do what I was looking for, display the digits from 0 to 1000 and back to 0 again but I have a few questions for you:

1. My digits only display on the first 3 LEDs (from left to right) instead of accross all 4. Where is this controlled in your sketch?

2. Can you explain what the function numOut is doing exactly?

3. What is the deal with the variable numStr?

4. Why exactly do we need stlib?

Again, thanks for all of the help on this!

JJ

So in order:

1. When the digits convert from a number to a char* array (a string), it will convert the number literaly to a string from left to right. When we iterate through the first four characters in the array, the first one is still on the far left hand side, and then we get to an unrecognized member of the array (the fantom fourth digit) which is likely failing in the program but fortunately does so in a way that still works. You could write a test in numOut for numbers less than 1000 to have a space written as the first digit.

When we send the character to digitOut, the digit is now not an int but a char, and so the letter 0 has a different value than the number 0. To convert the value of the char back to the int value, we subtract 48.

If anyone knows a simpler way to convert an int to an array witht the values of each digit in decimal as a number in the array, I'd love to hear it. I'm sure there are some recursive checks we could do to determine the value of each digit, but I thought this would be simpler. I don't really want to check each value, or divide each by 10, 100, 1000. This seems cleaner, but I'm sure there is a graceful mathematical solution.

The other thing that numOut does is provide the correct timing on the clock pulse for the digitOut function. On the fourth digit, the clock pulse is considerably longer. The long clock pulse causes the IC on the HC4LED board to display what is in memory. If we use the long pulse on each character, it will display each to the screen immediately and push the digit from right to left with each new character, giving a horible flicker at high speed, almost unreadable. The timing is something it took me a very long time to perfect, as it is hard to tell if the program is actually working correctly with bad timing (and the IC is fussy).

The variable numStr allocates enough space to contain the contents of our conversion from int to char. Because the function itoa() accepts a pointer as the target for the conversion, we are writing directly to RAM. Previously I allocated four characters, which I thought would be exactly enough, but it wasn't. When the program ran, other areas in memory would be overwritten and this caused the program to produce very strange output and eventually to crash. Therefore, I allocate more than enough memory (8 chars).

stlib provides the function itoa() which is integer to ascii, and then we access the ascii string as an array.



Jona

Okay, thanks for the explanations.

With that all being said (and read) I am still having trouble getting the output that I would like to see. For example I can't seem to get the display to show this "0101", or even this "0001".

Another question I have is related to the values that I am sending out to the display. In my current sketch, I am simply sending out the current value of the for loop control variable as it increments up to 1000. This is easy, but it doesn't show me how to add a value to another value and then send that to the display.

In my application, I am essentially counting the number of times a push button is pressed and displaying the appropriate number on the display. So if the display is currently at "1" and the button is pressed once, I need to update the display with a "2".

I am guessing I need to keep the existing value around in a variable so that I might use it for addition.

I know your input is coming from a thermistor, but I am wondering if you might have any insight into how my sketch might need to be written for my application?

Go Up