HELP with seven segment refresh

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

HELP with seven segment refresh

1,186 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pindonga123 on Sun Nov 03 05:33:18 MST 2013
Hello. I have some problems and some doubt about seven segment refresh. I did two programs. One shows me the number 0 to 9 in the 6 digit that i have, it work very well. And other program that show me a numer for example 576837, it dont work very well because the digit are tenuous. When I execute the two programs only shows me the first. Theory says that in the second case, each digit have to turn on each 1/6=166 mseg, but it dont work. How is the correct sequence?. Clear the display---Turn the display---Pause?, Is it the general rule?
Here my code

int main(void)
{
set_pines();
Set_SysTick ();
while(1)
{
parpadeo ();
mostrar_dig (564738);
}
return 0 ;
}

void Set_SysTick (void)
{
SYSTICK_InternalInit (1);
SYSTICK_Cmd (ENABLE);
SYSTICK_IntCmd (ENABLE);
}

void SysTick_Handler (void)
{
SysTickCnt++;
}

void Delay (float tick)
{
unsigned long systickcnt;
systickcnt = SysTickCnt;
while ((SysTickCnt - systickcnt) < tick);
}

void parpadeo (void)
{
signed int i, j;
for (i=0; i<=9; i++)
{
for (j=0; j<=1; j++)
{
GPIO_ClearValue (0, clear);
Delay (200);
GPIO_SetValue (0, vector_b0);
GPIO_SetValue (0, vector_b1);
GPIO_SetValue (0, vector_b2);
GPIO_SetValue (0, vector_b3);
GPIO_SetValue (0, vector_b4);
GPIO_SetValue (0, vector_b5);
Delay (200);
}
}
}

void mostrar_dig (unsigned long num)
{
int i=0, vect[6]; /*vect [6] lo declaraba como unsigned long y no funcionaba el programa. Porque?*/
while (num > 0)
{
vect=num%10;
num=num/10;
i++;
}
i--;
while (i>=0)
{
switch (i)
{
case 0:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b0[vect]);
i--;
Delay (1);
break;
case 1:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b1[vect]);
i--;
Delay (1);
break;
case 2:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b2[vect]);
Delay (1);
i--;
break;
case 3:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b3[vect]);
i--;
Delay (1);
break;
case 4:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b4[vect]);
i--;
Delay (1);
break;
case 5:
GPIO_ClearValue (0, clear);
GPIO_SetValue (0, vector_b5[vect]);
Delay (1);
i--;
break;
default:
break;
}
}
}
Labels (1)
0 Kudos
4 Replies

866 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Wed Nov 06 08:15:12 MST 2013

Quote: pindonga123
I have some doubts
1. about how I have to write the number.......Will be 0xXXXXXXXX or 1<<2 | 1<<6 |1<<10.....etc. I'll show you what I did for example to number four digit 0.
#define FOUR 1<<26 | 1<<2 | 1<<16 | 1<<23
#define B0 1<<9
uint32_t vector_B0 [10]={X,X,X, FOUR | B0, X,X,X,X,X,X}

I define a Macro with number 0 to 9, and a macro for each digit (I have 6 digit)



That is a very good way.
If you absolutely must write all the bits, you could do it by defining a macro for those bits you want to use.

For instance...
#define SSEG(a,b,c,d,e,f,g) (((a) << 10) | ((b) << 11) | ((c) << 15) | ((d) << 20) | ((e) << 21) | ((f) << 25) | ((g) << 26))


Note: I do not know which pins you've connected to which segment. You may have to re-arrange the bit positions, so that the parameters reflect this picture:
[img=220x220]http://upload.wikimedia.org/wikipedia/commons/thumb/0/02/7_segment_display_labeled.svg/220px-7_segme...

The paranthese around each argument (a), (b), etc.. are there to protect you from trouble if you use post/pre- increment/decrement. You'll learn about that later; it's not important right now, though.

Using the above macro, you can define macros further:
#define SSEG1 SSEG(0,1,1,0,0,0,0)
#define SSEG2 SSEG(1,1,0,1,1,0,1)
#define SSEG3 SSEG(1,1,1,1,0,0,1)
#define SSEG4 SSEG(0,1,1,0,0,1,1)
#define SSEG5 SSEG(1,0,1,1,0,1,1)
#define SSEG6 SSEG(1,0,1,1,1,1,1)
#define SSEG7 SSEG(1,1,1,0,0,1,0)
#define SSEG8 SSEG(1,1,1,1,1,1,1)
#define SSEG9 SSEG(1,1,1,1,0,1,1)
#define SSEG0 SSEG(1,1,1,1,1,1,0)

#define SSEG_MASK (SSEG1|SSEG2|SSEG3|SSEG4|SSEG5|SSEG6|SSEG7|SSEG8|SSEG9|SSEG0)
#define B_MASK (B0|B1|B2|B3|B4|B5)

Note: The last define would have the same value as SSEG8!


Quote: pindonga123
I dont know if it's correct or I have to put in Hexadecimal, but my problem is that i dont know for example if I control P0[0],  P0[2],  P0[10],  P0[11],  P0[15],  P0[20],  P0[21],  P0[25],  P0[26], put 1 or 0 in each one and write it in hexadecimal in a easy way, because my way is very complicated......In fact is a very primitive way......I write the 32 bits and put in each position 0 or 1 and after convert to hexadecimal. I have serious problem with the basic concept. Please help me



Programming is not about converting everything into cryptic writing.
It's about making it easy and clear for yourself to see what is going on, so you do not have to remember everything for the next time you see the code. Imagine you haven't looked at the code for 6 months, then you look at it again, and you'll have to figure out what's going on there.



Quote: pindonga123
2. Suppose you that I have P0[10]=1  P0[11]=1  P0[15]=1  P0[20]=1  P0[21]=1  P0[25]=1  P0[26]=1 controlling a display for example, and P0[0]  P0[1] controlling two led. If I wanna clear de display I need to write for example #define CLEAR 0x00000000. But always I call clear display and send the value 0x00000000 I'll turn off the led's and I dont wanna this happens



That's easy.

Above, we defined a mask.
Use...
LPC_GPIO1->FIOCLR = SSEG_MASK;

Now you have cleared all bits. If all 7-segment LEDs are now turned on, you might want to use this instead:
LPC_GPIO1->FIOSET = SSEG_MASK;


But what if you want to write a value *once*, instead of using two SET/CLEAR accesses ?
The LPC17xx has a solution for that as well.

When you want to start changing the 7-segment display, you set the FIOMASK to ~SSEG_MASK.
LPC_GPIO1->FIOMASK = ~(SSEG_MASK|B_MASK);/* enable only bits for 7-segment display */
LPC_GPIO1->FIOPIN = SSEG4 | B0;
LPC_GPIO1->FIOPIN = SSEG7 | B1;
LPC_GPIO1->FIOPIN = SSEG3 | B2;
...
LPC_GPIO1->FIOPIN = SSEG9 | B5;
LPC_GPIO1->FIOMASK = (SSEG_MASK|B_MASK);/* enable all other bits than the 7-segment bits */


You can then write the LED-values directly, for instance like this:
LPC_GPIO1->FIOPIN = LED1_ON | LED2_OFF;


You could also clear the mask completely to allow changing all bits:
LPC_GPIO1->FIOMASK = 0;/* enable all bits */



You can then use FIOSET and FIOCLR to flip your LED; or you can mask it manually, or you can use the FIOMASK if you like.
Here's an example on how you could set both LED1 and LED2 in one go by manually masking them into the previous value of the port bits:
LPC_GPIO1->FIOPIN = (LPC_GPIO1->FIOPIN & ~LED_MASK) | LED1_ON | LED2_OFF;




Quote: pindonga123
3.Why Do I need to do this?
for(j = i; j < DIGIT_COUNT; j++)
{
sDigitData[j] = ALL_OFF;
}

Why not this?
for(i=0; i < DIGIT_COUNT; i++)
{
sDigitData[j] = ALL_OFF;
}



In the code I attached above, the first few digits were already stored. When there are no further digits to store, there might be some left-overs from the last time we wrote digits in the buffers.
These left-overs should of course be cleaned up (so the digits are turned completely off).
You can clear everything *before* you start writing (which takes extra time), or you can clear only what's left to be cleared after writing.
I usually choose the latter, because the CPU then do not need to do as much work.



Quote: pindonga123
4.Why this
digit = vect;/* digit is a value 0..9 */
switch(i)
{
  case 0:
sDigitData = vector_b0[digit];
break;
}

And not this?
switch(i)
{
  case 0:
sDigitData = vector_b0[ vect ];
break;
}



Here we read vect into 'digit' once. This value usually ends up in one of the CPU's registers.
If not doing that, the CPU might read the table each time, that is a lot of unnecessary work and the binary file will be larger, resulting in that you are using more flash-memory for the program.
But actually, the real reason I did that, is that the code is much easier to read that way.
As a bonus it makes it less likely that you make errors, it's also much easier to make modifications.
(It's most of all a question of coding style).

Regarding your other questions...
void SysTick_Handler(void)/* This is an interrupt handler, it's called automatically. */
{
sSysTickCnt++;
}

void Delay(uint32_t aTicks)
{
uint32_tstart;
start = sSysTickCnt;/* read current value of sSysTickCnt */
while((sSysTickCnt - start) < aTicks);/* sSysTickCnt is incremented by the interrupt handler */
}

void Set_SysTick(void)/* this sets up the SysTick interrupt */
{
SYSTICK_InternalInit(1);
SYSTICK_Cmd(ENABLE);
SYSTICK_IntCmd(ENABLE);
}


SysTick is an interrupt which occurs around each 10 ms, which means approximately 100 times per second, thus it is a 100Hz interrupt.

In interrupts, your code should not loop. Eg...
avoid for(...), while(...), etc.
Interrupts should be fast; if they are not, it will feel like your CPU's performance is dropping to near zero.
0 Kudos

866 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by pindonga123 on Wed Nov 06 06:29:20 MST 2013
I have some doubts
1. about how I have to write the number.......Will be 0xXXXXXXXX or 1<<2 | 1<<6 |1<<10.....etc. I'll show you what I did for example to number four digit 0.
#define FOUR 1<<26 | 1<<2 | 1<<16 | 1<<23
#define B0 1<<9
uint32_t vector_B0 [10]={X,X,X, FOUR | B0, X,X,X,X,X,X}
I define a Macro with number 0 to 9, and a macro for each digit (I have 6 digit)
I dont know if it's correct or I have to put in Hexadecimal, but my problem is that i dont know for example if I control P0[0],  P0[2],  P0[10],  P0[11],  P0[15],  P0[20],  P0[21],  P0[25],  P0[26], put 1 or 0 in each one and write it in hexadecimal in a easy way, because my way is very complicated......In fact is a very primitive way......I write the 32 bits and put in each position 0 or 1 and after convert to hexadecimal. I have serious problem with the basic concept. Please help me
------------------------------------------------------------------------------------------------------------------------------------------
2. Suppose you that I have P0[10]=1  P0[11]=1  P0[15]=1  P0[20]=1  P0[21]=1  P0[25]=1  P0[26]=1 controlling a display for example, and P0[0]  P0[1] controlling two led. If I wanna clear de display I need to write for example #define CLEAR 0x00000000. But always I call clear display and send the value 0x00000000 I'll turn off the led's and I dont wanna this happens
-------------------------------------------------------------------------------------------------------------------------------------------
3.Why Do I need to do this?
for(j = i; j < DIGIT_COUNT; j++)
{
sDigitData[j] = ALL_OFF;
}
Why not this?
for(i=0; i < DIGIT_COUNT; i++)
{
sDigitData[j] = ALL_OFF;
}
-------------------------------------------------------------------------------------------------------------------------------------------------
4.Why this
digit = vect;/* digit is a value 0..9 */
switch(i)
{
  case 0:
sDigitData = vector_b0[digit];
break;
                 }
And not this?
switch(i)
{
  case 0:
sDigitData = vector_b0[ vect ];
break;
                }
0 Kudos

866 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Mon Nov 04 19:38:35 MST 2013
@pindonga123

Please add your email address to your account, so I can send you a PM.

To find out what variable sizes (declarations) you need to use, here's a simple guide:

[list][color=#030]
  [*]uint8_t can hold values between 0 and 255.
  [*]uint16_t can hold values between 0 and 65535.
  [*]uint32_t can hold values between 0 and 4294967295.
  [*]int8_t can hold values between -128 and 127.
  [*]int16_t can hold values between -32768 and 32767.
  [*]int32_t can hold values between -2147483648 and 2147483647.
[/color][/list]

-So yes, if you just need a variable and you don't know what size the numbers will be, just use int32_t for it.

If you try to fit a too large number in a too small variable, it'll be "clipped", here is an example:
uint8_t value;
value = 255;  /* value is now 255 */
value = 256;  /* value is now 0 */
value = 257;  /* value is now 1 */
value = 513;  /* value is now 1 */

...This is because only the low 8 bits are used. All the higher bits will be thrown away.

About interrupts:
You are already using a SysTick interrupt. This could easily be used to check if you have a button pressed. SysTick is probably also the easiest interrupt to use.

If you download the attachment from my first answer in this thread, you will find an example on setting up a timer interrupt. The file is located in the bottom of the box surrounding the message, look for the filename "SevenSegmentTest.c" and click it.

Using a timer-interrupt to check if a button is pressed, is possible.
It is also possible to set up the microcontroller so that an interrupt automatically occur when the button is pressed / released.
-But you should probably learn the first mentioned way first.
There are problems involved in both.

About the 1602 LCD displays: You will need to find the correct datasheet, the one that is written for exactly your model of the 1602 display. Various models exists, and the code for one display model might not work on a different model.

About the float in the Delay subroutine:
Using a float will not help you in this case.
Delay(0.43); will do exactly the same thing as Delay(0);
Delay(0.99999); will still be like Delay(0);
Delay(1.99999); will be like Delay(1); and so forth.

So the result will be the same if you change it to uint32_t.
The code, however, will be shorter when using integers (uint32_t is an integer) instead of floats.

Note: SysTick is executed 100 times per second, so you will have a precision of approximately 10 milliseconds.
If you need something more precise than that, you'll need to set up a timer. (Again, download the example from my first answer).
0 Kudos

866 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Mon Nov 04 04:48:11 MST 2013
First of all, if you're not doing so already, consider using a driver on your LEDs, to make sure that the digit '8' does not look dimmed, compared to the digit '1'.

A driver could be as simple as a BC547 transistor with a 100 ohm resistor on the collector and a 1K resistor on the basis. But it would be expensive to use transistors if you are designing this for production. Instead, a driver IC would be a good choice.


Second: Please use the CODE tag, when you post code snippest, it looks like this (without the spaces):
[ code ]
your code snippet
[ /code ]

...It makes the code much more readable, and it is more likely that you will receive a reply.

Third, this is probably a little difficult to explain, but I'll try.

When you are turning on one digit on a single display, all the necessary pins are turned on. You will see the digit fully visible.
If two displays are sharing some pins and you switch between two digits on these two displays, you will see that the digits gets much darker.
Three digits gets even darker.
If you have a pause / delay after you have switched through all digits, you will see approximately nothing.
This is because the on/off ratio is very close to 'always off'.

Now, in order to make sure your digits on your displays are visible, you must keep switching between the digits, and make sure that they all have the same delay between each switch.

Your code...
Delay (200);

...makes sure you will see nothing but one digit on the display.

So: [color=#c00]NO PAUSE and NO DELAY[/color] after you've displayed the digits.

You should, however, like you do right now, have delays between the digits.
But the delay between going from the last digit to the first digit, should be the same as the delay for  all the other digits.

To archieve this, you can't really use a main-loop. It would be a bad idea.
Instead, use an interrupt for it. A millisecond interrupt would be fine (eg. executed more than NDIGITS * 100 times per second).
Using a 1000 Hz timer for 6 digits would be a good idea, then you wouldn't get any annoying flickering.

In your interrupt, you should write the pin values directly instead of using set and clear. Example for the LPC1769:
LPC_GPIO0->FIOPIN = vector_b0[vect];


Other hints:
You do not want to use a float in your Delay routine:
void Delay (float tick)
{
unsigned long systickcnt;
systickcnt = SysTickCnt;
while ((SysTickCnt - systickcnt) < tick);
}


I recommend using integers everywhere in your code.
The only places you should use floats are when you require heavy calculations that can not be done by using integers.
I'd also like to recommend you to prefix your variables and parameters. See below.

void Delay(uint32_t aTicks)
{
uint32_tstart;
start = sSysTickCnt;
while((sSysTickCnt - start) < aTicks);
}


For prefixes I use:
[list]
  [*]'a' in front of all argument names, then 'CamelHump' style casing.
  [*]'s' for static variables (eg. variables that are local to the file or function using the [color=#00f]static[/color] keyword)
  [*]'g' for global variables.
  [*]'m' for member variables (if writing C++).
[/list]

For variables that are local to a function/subroutine, first character is lowercase.
#define MACROS are all uppercase.
Structures start with a capital letter using CamelHumpStyle.


[color=#00f]Now, please download the attachment from below, and have a look at how the problem could perhaps be solved.[/color]
The code is not tested, as I don't have your setup.
0 Kudos