## In switch/case can a range be used for case

24 posts / 0 new
Author
Message

Have a 7-segment led display I'm trying to obtain the tens digit for. Here is the code.

```    if      (time > 89) t = _7seg_doit(9);
else if (time > 79) t = _7seg_doit(8);
else if (time > 69) t = _7seg_doit(7);
else if (time > 59) t = _7seg_doit(6);
else if (time > 49) t = _7seg_doit(5);
else if (time > 39) t = _7seg_doit(4);
else if (time > 29) t = _7seg_doit(3);
else if (time > 19) t = _7seg_doit(2);
else if (time >  9) t = _7seg_doit(1);
else                t = _7seg_doit(0);
```

I though there is a more elegant way to do this using switch/case with a range for the case portion like here

```  switch(time)
{
case 90-99:
t = 9;
break;
case 80-89:
t = 8;
break;
case 70-79:
t = 7;
break;
case 60-69:
t = 6;
break;
case 50-59:
t = 5;
break;
case 40-49:
t = 4;
break;
case 30-39:
t = 3;
break;
case 20-29:
t = 2;
break;
case 10-19:
t = 1;
break;
case 0-9:
t = 0;
break;
}
```

Seems to me I have used something like that. May be it was in Turbo Pascal or Delphi.

My understanding is that the case only can use a constant. I'm hoping a range numbers or characters can be used. Any ideas?

Mike[/code]

Quote:

Would you consider dividing by ten, and eliminating about 95% of your horrific code, like so:

`    t = sevenSeg_doit(time / 10);`

?

That's all for now, but stay tuned next week for details on how to replace a 256-case switch statement for generating numbers one greater than an arbitrary unsigned character value with a single clever use of a "+ 1" operation.

That's all for now, but stay tuned next week for details on how to replace a 256-case switch statement for generating numbers one greater than an arbitrary unsigned character value with a single clever use of a "+ 1" operation.

Can't wait.

Right out of the gate I used % to get the units with no problems and divide like you did to get the tens but for some reason the tens were not working. I thought (there is where I made another mistake) well, the result is being rounded up or down and dug into a sure fire way to get the result needed and down the marry path I went, do-dah do-dah. Thanks that cleaned up a nasty looking part of my code.

Oh yes, there is a Case in Pascal and it is Case without Switch and they can take a range for the argument. As in

Case Number of
1..10 : WriteLn (â€™Small numberâ€™);
11..100 : WriteLn (â€™Normal, medium numberâ€™);
else
WriteLn (â€™HUGE numberâ€™);

Thanks, especially for the quick reply,
Mike
:D

You can use div() (or ldiv() for longs) and do the divide and the mod in one step.

Regards,
Steve A.

The Board helps those that help themselves.

Quote:

May be it was in Turbo Pascal or Delphi.

Yup, it most likely was, since Pascal do allow what you are asking for.

Pascal and C switch/case differs in more than just that. A Pascal case-statement is more like a cascaded if-statement. A C switch-statement is more like a multiple goto.

In C, a case that does not do a break will fall through to the next case. In Pascal that will not happen.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

As Levenkay has suggested, a simple division is all done in one line !

However, you original construction is absolutely fine for 'non-linear' lookups.

If you wanted to use a switch statement, you would need:

```  switch(time)
{
case 90:
case 91:
case 92:
...
case 98:
case 99:
t = 9;
break;
...
```

Some C compilers may convert your 100-case switch into very intelligent code.
You could try it with avr-gcc. I would not hold my breath.

If you are multiplexing the LED display, it is far more efficient to have a display buffer.
Write the actual pattern into each byte of the buffer. (Only executed when the display changes)
The multiplex routine simply loads display[n] into the n'th 7-segement. No divisions are involved.

David.

Steve A. wrote:
You can use div() (or ldiv() for longs) and do the divide and the mod in one step.
Didn't know of div() and ldiv(). Comparing 'div()' vs '/', the '/' is 52 bytes smaller so that is what I went with.
JohanEkdahl wrote:
Pascal and C switch/case differs in more than just that. A Pascal case-statement is more like a cascaded if-statement. A C switch-statement is more like a multiple goto.
I was not aware of the close but slight differences, thinking they were the same, I used whatever syntax that came to mind. You can do that but don't expect the code to compile.
david.prentice wrote:
If you are multiplexing the LED display, it is far more efficient to have a display buffer.
Write the actual pattern into each byte of the buffer. (Only executed when the display changes)
The multiplex routine simply loads display[n] into the n'th 7-segement. No divisions are involved.
Thats what I'm doing

```// 7segtest.c

//#include
#include
#include "/home/mike/mfile/code/include/7segment.h"

volatile unsigned int interruptscount = 0;
volatile unsigned int time = 0;
volatile unsigned int units = _0;
volatile unsigned int tens = _0;

// Timer 0 Overflow Interrupt Handler
ISR(TIMER0_OVF0_vect)
{
interruptscount++;
if (interruptscount == 11)
{
interruptscount = 0;              // Clear interrupt counter
if (++time == 100) time = 0;
units = _7seg_dispat(time % 10);  // Convert number to display pattern
tens  = _7seg_dispat(time / 10);  // Convert number to display pattern
}
}

int main(void)
{
_7seg_init();

while(1)
{
_7seg_display(tens, units);         // Send display pattern to 7-segment led
}
}
```
```// 7segment.h

// Library for 7segment.c

#include
#include
#include

#define a       1                           // 7-seg 'a' pin
#define b       2                           // 7-seg 'b' pin
#define c       4                           // 7-seg 'c' pin
#define d       8                           // 7-seg 'd' pin
#define e       16                          // 7-seg 'e' pin
#define f       32                          // 7-seg 'f' pin
#define g       64                          // 7-seg 'g' pin
#define dp      128                         // 7-seg 'dp' pin

#define _0      (a | b | c | d | e | f)     // Display Pattern 0
#define _1      ( b | c)                    // Display Pattern 1
#define _2      (a | b | d | e | g)         // Display Pattern 2
#define _3      (a | b | c | d | g)         // Display Pattern 3
#define _4      (b | c | f | g)             // Display Pattern 4
#define _5      (a | c | d | f | g)         // Display Pattern 5
#define _6      (a | c | d | e | f | g)     // Display Pattern 6
#define _7      (a | b | c )                // Display Pattern 7
#define _8      (a | b | c | d | e | f | g) // Display Pattern 8
#define _9      (a | b | c | f | g)         // Display Pattern 9
#define _tens   1                           // Tens On Pin
#define _units  2                           // Units On Pin
#define _both   0                           // Tens and Units Off

#define disPORT PORTB                       // Display PORT
#define disDDR  DDRB                        // Display DDR
#define ctlPORT PORTD                       // Control PORT
#define ctlDDR  DDRD                        // Control DDR

// Initialize CPU
void _7seg_init(void);
// Convert numberical value to Display Pattern
int  _7seg_dispat(unsigned int s);
// Send tens and units Display Patterns to 7-segment LED
void _7seg_display(int dtens, int dunits);

// Initialize CPU
void _7seg_init(void)
{
ctlDDR  = 0xff;   // ControlDDR
ctlPORT = _both;  // ControlPORT
disDDR  = 0xff;   // DisplayDDR
disPORT = 0x00;   // DisplayPORT

TIMSK = (1 << TOIE0);               // T/C0 Overflow Interrupt Enable
TCCR0 = (1 << CS02) | (1 << CS00);  // CK/1024

asm("sei");       // Enable Interrupts
}

// Convert numberical value to display pattern
int _7seg_dispat(unsigned int s)
{
int x = 0;
switch(s)
{
case 0:  x = _0;  // Display Pattern (a | b | c | d | e | f)
break;
case 1:  x = _1;  // Display Pattern ( b | c)
break;
case 2:  x = _2;  // Display Pattern (a | b | d | e | g)
break;
case 3:  x = _3;  // Display Pattern (a | b | c | d | g)
break;
case 4:  x = _4;  // Display Pattern (b | c | f | g)
break;
case 5:  x = _5;  // Display Pattern (a | c | d | f | g)
break;
case 6:  x = _6;  // Display Pattern (a | c | d | e | f | g)
break;
case 7:  x = _7;  // Display Pattern (a | b | c )
break;
case 8:  x = _8;  // Display Pattern (a | b | c | d | e | f | g)
break;
case 9:  x = _9;  // Display Pattern (a | b | c | f | g)
}
return x;
}

// Send tens and units display patterns to 7-segment led
void _7seg_display(int dtens, int dunits)
{
int dly = 2;

_delay_ms(dly);     // Delay
disPORT = dtens;    // Tens to DisplayPORT
ctlPORT = _tens;    // Tens LED On
_delay_ms(dly);     // Delay
ctlPORT = _both;    // Tens LED Off
disPORT = dunits;   // Units to DisplayPORT
ctlPORT = _units;   // Units LED On
_delay_ms(dly);     // Delay
ctlPORT = _both;    // Tens LED OFF
_delay_ms(dly);     // Delay
}
```

Thanks guys,
Mike

here is mine, maybe a little bit more structured than yours:

```void dig_display(unsigned char dig, unsigned char n) {		//display a number
const unsigned char seg_table[] = {
0xc0,										//0
0xf9,
0xa4,
0xb0,
0x99,
0x92,
0x82,
0xf8,
0x80,
0x90
};

dig_off(DIGs);								//turn off all digits
SEG_PORT = seg_table[n % 10];				//display the digit
dig_on(dig);								//turn on the digit
delay_ms(DIG_DLY);							//waste some time
dig_off(DIGs);								//turn off all digits
}

void time_display(void) {						//display the current time
//display the current second
dig_display(DIG0, time.sec % 10);			//display the lowest digit of second
dig_display(DIG1, time.sec/10);				//display the 10s digit of second

//display the current min
dig_display(DIG2, time.min % 10);			//display the current hour
dig_display(DIG3, time.min/10);				//display the current hour

//display the current hour
dig_display(DIG4, time.hour % 10);
dig_display(DIG5, time.hour/10);
}
```

dig_display() is a generic display function for a given digit. time_display() shows all 6 digits (or more if you want), and is fairly reconfigurable.

I would disagree with both of you.

Use your Timer ISR() to do the multiplexing, and updating the display buffer.

```ISR(TIMER0_COMPA_vect)              // multiplex every 10ms
{
if (interruptcount & 1) {
disPORT = dtens;    // Tens to DisplayPORT
ctlPORT = _tens;    // Tens LED On
} else {
disPORT = dunits;    // Units to DisplayPORT
ctlPORT = _units;    // Unitss LED On
}
interruptscount++;
if (interruptscount == 100)         // update display every second
{
interruptscount = 0;              // Clear interrupt counter
if (++time == 100) time = 0;
units = _7seg_dispat(time % 10);  // Convert number to display pattern
tens  = _7seg_dispat(time / 10);  // Convert number to display pattern
}
}
```

If you are multiplexing say 8 seven-segments, you would have a 8 byte display buffer:

```  static uint8_t idx;     // display index
if (++idx > 7) idx = 0; // 0 .. 7
disPORT = dispbuf[idx]; // send digit to DisplayPORT
ctlPORT = 1 << idx;     // enable selected digit
```

You will find that the compiler generates very efficient code for the multiplex.

Likewise, you can have a very simple lookup table.

```uint8_t lookup[10] = { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9 };

void put_7seg(uint8_t idx, uint8_t value)
{
dispbuf[idx] = lookup[value];
}
```

David.

no display buffer but all done in isrs:

```//tmr1 isr
ISR(TIMER1_OVF_vect) {							//timer 1 overflow isr
static unsigned long time_elapsed=0;

time_elapsed += ISR_INTERVAL;				//time elapsed since the last interrupt
TCNT1 += -ISR_INTERVAL;
if (time_elapsed >= (DISP_INTERVAL*1000000ul)) {		//update time
time_elapsed -= (DISP_INTERVAL*1000000ul);			//update time_elapsed
time.sec+=DISP_INTERVAL;							//increase the second.
if (time.sec>=60) {
time.sec-=60;						//update the second
time.min+=1;						//update the min
if (time.min>=60) {
time.min-=60;					//update the min
time.hour+=1;					//update the hour
if (time.hour>=24) time.hour-=24;
}
}
}
}

//tmr3 isr
ISR(TIMER3_OVF_vect) {							//timer 3 overflow isr
static unsigned long current_digit=0;		//remember current digit to be displayed
unsigned char dig, n;						//for display

const unsigned char seg_table[] = {
0xc0,										//0
0xf9,
0xa4,
0xb0,
0x99,
0x92,
0x82,
0xf8,
0x80,
0x90
};

TCNT3 += -(DIG_DLY*1000);										//load tcnt3 with offset
switch (current_digit++) {
case 0:	dig = DIG0; n = time.sec % 10; break;				//display digit0
case 1:	dig = DIG1; n = time.sec / 10; break;				//display digit1
case 2:	dig = DIG2; n = time.min % 10; break;				//display digit2
case 3:	dig = DIG3; n = time.min / 10; break;				//display digit3
case 4:	dig = DIG4; n = time.hour % 10; break;				//display digit4
case 5:	dig = DIG5; n = time.hour / 10; break;				//display digit5
default: current_digit=0;									//reset current display counter
}

dig_off(DIGs);								//turn off all digits
SEG_PORT = seg_table[n % 10];				//display the digit
dig_on(dig);								//turn on the digit
//delay_ms(DIG_DLY);							//waste some time
//dig_off(DIGs);								//turn off all digits

}```

tmr1 keeps time; and tmr3 updates the display, at DIG_DLY ms intervals.

tmr3 is essentially the same code I provided earlier (time_display()), isr'zed.

the macros are provided to allow more flexibility.

see it in action.

## Attachment(s):

millwood wrote:
no display buffer but all done in isrs:

Are you sure, that only 6 bytes SRAM are to expensive?

But then you need no division inside every interrupt and thus the interrupt should be over 10 times faster. :!:

Peter

Brewski wrote:
I though there is a more elegant way to do this using switch/case with a range for the case portion
It's not a best choice for extracting high digit from two-digit value, but...

gcc (and avr-gcc) can handle ranges in case.
There is a GNU extension to C language (btw, for Pascal-mans, gcc extension list includes local functions)

```#include

uint8_t foo(uint8_t i)
{
switch(i) {
case 0 ... 5:   return 1; // spaces around '...' are mandatory
case 10 ... 20: return 2;
default:        return 0;
}
}```
```foo:
cpi r24,lo8(6)
brlo .L3
subi r24,lo8(-(-10))
cpi r24,lo8(11)
brsh .L2
ldi r24,lo8(2)
ret
.L3:
ldi r24,lo8(1)
ret
.L2:
ldi r24,lo8(0)
ret```

wbr, ReAl

Quote:

gcc (and avr-gcc) can handle ranges in case.
There is a GNU extension to C language

Well-well, you learn something every day (hopefully :D).

If portability is at all an issue I'd stay away from both that ellipsis, and the local functions.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

JohanEkdahl wrote:
Well-well, you learn something every day
You don't ???
All time is spent for "portability" mantra?
JohanEkdahl wrote:
If portability is at all an issue I'd stay away from both that ellipsis, and the local functions.
If

`OCR1A += 100;`

even less portable - just between compilers with AVR support.

for avr-gcc toolchain
atomic.h is not portable to other compilers

pgmspace.h is portable neither to other compilers, nor to other micros - shall I use strings in RAM to avoid non-portable %S format specifier?

pointer arithmetic is "not portable" to programmers that do not understand it well, so many "portability teachers" suggest to avoid it at all for "portability" with all C-"programmers":(

"range in case" is portable from any gcc-supported system to any gcc-supported system. That system count is much more than I can learn (and than even you can learn).

AVR, ARM*, may be MSP430 and PIC24 for embedded and Win/Lin for PC is enough for me. It is not planned to go back to mcs51 and pic16. So, portability of GCC features is good enough.

wbr, ReAl

Quote:

"range in case" is portable from any gcc-supported system to any gcc-supported system. That system count is much more than I can learn (and than even you can learn).

As is computed goto. (afaik) That doesn't mean it is a "good thing" for purists.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Quote:
Didn't know of div() and ldiv(). Comparing 'div()' vs '/', the '/' is 52 bytes smaller so that is what I went with.
But '/' only gives you the tens digit. You still need a '%' to get the ones digit. Doing 'div()' gives you both in one operation.

Regards,
Steve A.

The Board helps those that help themselves.

Or if this is a human-readable real-time clock, the time could be kept in human-readable form to begin with to avoid div/mod operations at all. A byte stores two decimal digits (BCD).

theusch wrote:
As is computed goto. (afaik)
Yes! I use it for protothreads!

theusch wrote:
That doesn't mean it is a "good thing" for purists.
Purists...
atomic.h has no "range in case", has no local functions, has no computed goto. But it is not portable to IAR/AVR. Where were purists?
"Câ€™est La Vie"

I can write portable programs -- when it is really needed.

wbr, ReAl

Koshchi wrote:
Quote:
Didn't know of div() and ldiv(). Comparing 'div()' vs '/', the '/' is 52 bytes smaller so that is what I went with.
But '/' only gives you the tens digit. You still need a '%' to get the ones digit. Doing 'div()' gives you both in one operation.

On my computer using the gcc-gnu avr-libc library the code below created a program 52 bytes smaller than using div()
```    units = time % 10;
tens  = time / 10;
```

The time only counts up to 100. When time reaches 100 time is reset to zero.

div() returns a structure
"returns" mean:

• caller creates (reserves space for) div_t object on stack
• caller put the object address as first hidden argument to div()
• div() fills the structure object
• caller gets result from structure on stack
Helper functions for / and % operators returns result in registers and code size is less than for div(). But div() is faster than / plus %

For small counter you can use two bytes for counter:

```uint8_t second_uints, second_tens;
if( ++second_units >= 10 ) {
second_units = 0;
if( ++second_tens >= 6) {
secnod_tens = 0;
// handle minutes
}
}```

and remove / % div() at all.

wbr, ReAl

ReAlAl wrote:

For small counter you can use two bytes for counter:

Or even smaller with BCD which stores one digit per nibble like my previous suggestion. The other advantages remain, like being very easy to convert time to 7-segment or ASCII.

```uint8_t bcd_seconds=0;

bcd_seconds++; // increase ones

// if ones hit 0d10(0x0A), make ones 0 and increase tens 0x10(0d16)
if ((bcd_seconds&0x0F)==0x0A) bcd_seconds+=6;
// if tens hit 6 and seconds 0 (0x60) minute passes
if (bcd_seconds==0x60)
{
bcd_seconds=0;
// handle minutes
}

// write seconds as ASCII letters to LCD or UART
send('0'+(bcd_seconds>>4));
send('0'+(bcd_seconds&0x0F));

// write seconds to 7-segment
digits[0]=bcd_seconds>>4;
digits[1]=bcd_seconds&0x0F;
setseg(0,digittable[digits[0]];
setseg(1,digittable[digits[1]];
```

This is RAM/FLASH size balance

```setseg(0,digittable[ second_tens ];
setseg(1,digittable[ second_units ]; ```

requires one RAM byte more per counter but slightly shorter in flash than

```setseg(0,digittable[ bcd_seconds>>4 ];
setseg(1,digittable[ bcd_seconds&0x0F ];```

(assume that digittable[] is placed in flash and pgm_read_byte() is used because digittable[] consumes even more RAM :-) )

wbr, ReAl

I dealing with a clock that is dealing with a speed of seconds. With a 4.0MHz clock that is plenty of speed to do the seconds counts and display that count on a 2 digit, 7-segment LED display. Although it can display 0 to 99 seconds I'm only interested in 60 seconds. I don't even care about minutes I know that 90 seconds in 1 and 1/2 minutes. What I interested in how long, in seconds, until the next exercise set. "%" and "/" works very well for me taking less memory that div() plus to me the code is much cleaner than anything else that I have seen here. You guys can argue your views here until hell freezes over but it is MY code, MY program, and with or without your approval or not I WILL do what I please NOT what YOU want me to.

I don't know why my original "/" did not work, that code has be wiped out. Probably a problem somewhere else in my code. Who knows. I don't care. The problem is fixed. On to more problems to code. Thanks to all those that helped me out...

Some of you need to get a life.

Nothing like beating a horse to death,
Mike

Brewski wrote:
"%" and "/" works very well for me taking less memory that div()

And I explained why and how it costs in time.
When code speed will be important, maybe you remember about the div().

Brewski wrote:
it is MY code, MY program, and with or without your approval or not I WILL do what I please NOT what YOU want me to.
So, why you ask questions here?

Brewski wrote:
I don't know why my original "/" did not work, that code has be wiped out.
It is YOUR code. Good weekend to you. I'm going to work with MY code.

wbr, ReAl