Jump to content
RobG

MSP430 LaunchPad, 74HC595, Four 7 Segment Displays

Recommended Posts

****************

For the latest, see this post

****************

 

In this example, I have replaced LEDs with four 7 segment displays.

 

Note: I am using HC series, not HCT. LaunchPad provides 3.6V, HCT needs 5V.

 

 

#include 

unsigned int counter = 0;                   // Counter variable
unsigned int digitCounter = 0;				// Digit counter
unsigned char digit = 0;					// Single digit to be displayed
unsigned char bcd7digit[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; // BCD to 7 digit map
unsigned char digitSelector[4] = {0x01, 0x02, 0x04, 0x08}; // Digit selector map

void main(void)
{
 WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

 P1OUT |= 0x01; 							// Port P1.0 will be used to latch
 P1DIR |= 0x01;

 USICTL0 |= USIPE6 + USIPE5 + USIMST + USIOE; // Out & clk enable, SPI Master
 USICTL1 |= USICKPH + USIIE;               // Counter interrupt, flag remains set
 USICKCTL = USIDIV_7 + USISSEL_2;          // /128 SMCLK
 USICTL0 &= ~USISWRST;                     // USI released for operation
 USICNT = USI16B;                     		// Enable 16 bit

 CCTL0 = CCIE;                             // CCR0 interrupt enabled
 CCR0 = 500;								// 
 TACTL = TASSEL_2 + MC_1 + ID_3;           // SMCLK, upmode

 _bis_SR_register(LPM0_bits + GIE);        // Enter LPM0 w/ interrupt
}

// Timer A0 interrupt service routine
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A (void)
{	
 digitCounter++;        					// Increase digit counter
 digitCounter &= 0x03; 					// Mask, counter range is 0-3
 digit = counter>>(4 * digitCounter); 		// Shift digits right
 digit &= 0x0F; 							// Mask, we need first digit only
 USISRL = bcd7digit[digit]; 				// Get segments from the map
 USISRH = digitSelector[digitCounter]; 	// 
 if(digitCounter == 0) {
 	counter = _bcd_add_short(counter, 0x01);// Decimally increase counter's when on first digit
 }
 USICNT |= 16;                           	// Start USI
}

// USI interrupt service routine
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
 USICTL1 &= ~USIIFG;               		// Clear pending flag
 P1OUT &= ~0x01;							// Latch data
 P1OUT |= 0x01;
}

post-197-135135493364_thumb.png

Share this post


Link to post
Share on other sites

Posted the video.

 

BTW, my schematic shows 4 single digit displays, but on my video, I am using one 4 digit display from Futurlec.

I chose that one because it requires less connections.

Also, I am using 74HC595 instead of 74HCT595 which is listed on the diagram.

Resistors are 220 ohms.

Share this post


Link to post
Share on other sites

I was asked to redo my code to take 8 switches, convert the binary value to BDC, and then display that on the 7 segment display.

Also, since we will only use 4 displays, I got rid of the second 595 and ULN2003 and simply used 4 transistors instead.

 

 

#include 

#define BIT1_4 (BIT1 + BIT2 + BIT3 + BIT4)	// all digit bits

unsigned int data = 0;						// data to be displayed
unsigned int digitCounter = 0;				// digit counter
unsigned char digit = 0;					// single digit to be displayed
const unsigned char hex7digit[16] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x58,0x5E,0x79,0x71}; // hex to 7 digit map
const unsigned char digitSelector[4] = {0x02, 0x04, 0x08, 0x10}; // digit selector map P1.1-P1.4

// BITn of switchReady and switchStatus corresponds to Port P1.n
unsigned char switchReady = 0;				// when BITn is 0, means switch is unstable H->L or L->H
unsigned char switchStatus = 0;				// pressed or released

void main(void)
{
WDTCTL = WDTPW + WDTHOLD;				// stop WDT

P1OUT |= BIT0;							// port P1.0 used to latch 74HC165 and 74HC595
P1DIR |= BIT0;
P1OUT &= ~BIT1_4;						// port P1.1-P1.4 used to drive digits
P1DIR |= BIT1_4;

USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIMST + USIOE; // in, out & clk enable, SPI Master
USICTL1 |= USICKPH + USIIE;				// counter interrupt, flag remains set
USICKCTL = USIDIV_4 + USISSEL_2;		// /16 SMCLK
USICTL0 &= ~USISWRST;					// USI released for operation

CCTL0 = CCIE;							// CCR0 interrupt enabled
CCR0 = 625;								// approx. 5ms 
TACTL = TASSEL_2 + MC_1 + ID_3;			// SMCLK/8, upmode

_bis_SR_register(LPM0_bits + GIE);		// LPM0 with interrupt

}

// Timer A0 interrupt service routine
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A (void)
{	

if(digitCounter == 0) {					// update switch status every 20 ms
	switchReady = ~(USISRL ^ switchStatus);	// copy USI's SRL register to switchReady and switchStatus
	switchStatus = USISRL;
	if(switchReady == 0xFF) {				// when all 8 switches are ready...	
		unsigned int binary = switchStatus;	// copy switchStatus to temporary variable
		data = 0;							// clear data
		unsigned int multiplier = 1;		// temporary multiplier variable
		while(binary > 0) {					// convert binary to bcd
			data += (binary % 10) * multiplier;	// add reminder * multiplier to data
			binary /= 10;
			multiplier *= 16; 
		}
	}
}

digitCounter++;							// increase digit counter
digitCounter &= 0x03;					// mask, counter range is 0-3
digit = data>>(4 * digitCounter);		// shift digits right
digit &= 0x0F;							// mask, we need first digit only
USISRL = hex7digit[digit];				// get segments from the map
USICNT |= 8;							// start USI
}

// USI interrupt service routine
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
USICTL1 &= ~USIIFG;						// clear pending flag
P1OUT &= ~BIT1_4;						// turn off all digits
P1OUT &= ~BIT0;							// latch data display (& input for next timer interrupt)
P1OUT |= digitSelector[digitCounter];	// turn on digit
P1OUT |= BIT0;
}

 

post-197-135135494306_thumb.png

Share this post


Link to post
Share on other sites

Hi Rob (and gang),

 

May I ask you a couple questions about your project, please?

 

(1) Are your switches configured with pull-up resistors for an "active low" signal when the switch is closed?

 

(2) Are you using "active high" signals (non-inverted data from USISRL) when calculating the BCD display data? That is, when you display "0001" on the display, are all the DIP switches in the "on" position except for the "1" (binary 2^0 weighted) switch?

 

Thank you for sharing a very nice example project.

 

Cheerful regards, Mike McLaren, K8LH

Share this post


Link to post
Share on other sites

Good catch Mike.

Those resistors should be pulling low and the switches should be active high. As it is on the schematic, to be inactive all switches have to be closed drawing unnecessary current. To fix it, resistors should be connected to GND and switches to Vcc.

 

73

 

Rob K1WAW

Share this post


Link to post
Share on other sites
Good catch Mike.

Those resistors should be pulling low and the switches should be active high. As it is on the schematic, to be inactive all switches have to be closed drawing unnecessary current. To fix it, resistors should be connected to GND and switches to Vcc.

I would just leave the switches active low and invert USISRL bits where needed in the code.

 

Got another question if you don't mind. What's the purpose of the following code in the Timer A ISR, please?

 

      switchReady = ~(USISRL ^ switchStatus);   // copy USI's SRL register to switchReady and switchStatus
     switchStatus = USISRL;
     if(switchReady & 0xFF) {            // when all 8 switches are ready...   

At first glance I thought you were just detecting change-of-state and skipping the Bin-to-Dec code if "no change" but the logic seems to be doing something else.

 

Cheerful regards, Mike

Share this post


Link to post
Share on other sites
I would just leave the switches active low and invert USISRL bits where needed in the code.

That will work too.

Got another question if you don't mind. What's the purpose of the following code in the Timer A ISR, please? ... At first glance I thought you were just detecting change-of-state and skipping the Bin-to-Dec code if "no change" but the logic seems to be doing something else.

De-bouncing. We check switches every 20ms and if there is a change in switch's position, most likely switch was pushed/released and it's bouncing. We mark it ready when the current position is the same as the last one.

Share this post


Link to post
Share on other sites
Got another question if you don't mind. What's the purpose of the following code in the Timer A ISR, please? ... At first glance I thought you were just detecting change-of-state and skipping the Bin-to-Dec code if "no change" but the logic seems to be doing something else.

        switchReady = ~(USISRL ^ switchStatus);  //
       switchStatus = USISRL;			          //
       if(switchReady & 0xFF) {		           // when all 8 switches are ready...   

De-bouncing. We check switches every 20ms and if there is a change in switch's position, most likely switch was pushed/released and it's bouncing. We mark it ready when the current position is the same as the last one.

I was wondering if that's what you were trying to do while watching the '0' (change-of-state) bits in the "switchReady" variable during simulation as they hang around for one 20-msec interval.

 

Is there a chance your "if" statement logic could be faulty? In simulation it's not skipping over the Bin-to-Dec code when there's a '0' (change-of-state) bit in "switchReady". How about something like this (below)? The change-of-state (xor) logic works with normal or inverted USISRL data but if you use inverted data your active-low DIP switches will now be '0' = "off" and '1' = "on".

 

 /*
  *           ___----____----____----_____     invert active low inputs
  *    swlat  ____----____----____----____     switch state latch
  *    delta  ___-___-___-___-___-___-____     changes, press or release
  *
  *    delta = ~USISRL ^ swlat;             // changes, press or release
  *    swlat ^= delta;                      // update switch state latch
  *    if(delta == 0) {                     // if not change-of-state
  */
     switchReady = ~USISRL ^ switchStatus;  // changes, press or release
     switchStatus = ~USISRL;                // update switch state latch
     if(switchReady == 0) {                 // if switches 'stable'
     }

As for debouncing, sampling at 20-msec intervals should be adequate for debouncing both the press and release states. If you happen to sample a bouncing pressed switch at the "pressed" state you're good because it will certainly have stopped bouncing by the time you sample it again 20-msecs later. On the other hand if you sample a bouncing pressed switch at the "release" state, that's fine too because it will certainly be done bouncing when you sample it again 20-msecs later at the "pressed" state. You're talking about a very decent response time ranging from 20 to 40 msecs. And that begs the question, why bother with the "if" statement at all?

 

Anyway, please forgive me for the critical analysis. I love your program. Your simple and elegant SPI method for driving both latches is genius.

 

Cheerful regards, Mike McLaren

Share this post


Link to post
Share on other sites
Is there a chance your "if" statement logic could be faulty? In simulation it's not skipping over the Bin-to-Dec code when there's a '0' (change-of-state) bit in "switchReady".

Could this have something to do with the inverting of USISRL you have proposed earlier? The only thing that should be changed is switchStatus = USISRL to switchStatus = ~USISRL

 

switchReady = ~(USISRL ^ switchStatus);  // compare current switch positions with previous, 
                                                                  // difference will result in 1, invert, 0 means switch not ready
switchStatus = USISRL;                   // save current switch positions, use them when ready flag is 1, 
                                                   // in next round this will be read as previous
if(switchReady & 0xFF) {                 // when all 8 switches are ready 

 

Here's how it works (first value is switchReady, second switchStatus (or position,) switches are off)

11111111

00000000

Now we flip one switch 00000001

11111110 (0 means switch is not ready, bouncing, switchReady & 0xFF is false)

00000001

Then on the next round

11111111 (1 means switch is ready, switchReady & 0xFF is true)

00000001

Now we switch it off 00000000

11111110

00000000

And next

11111111

00000000

 

As for debouncing, sampling at 20-msec intervals should be adequate for debouncing both the press and release states. If you happen to sample a bouncing pressed switch at the "pressed" state you're good because it will certainly have stopped bouncing by the time you sample it again 20-msecs later. On the other hand if you sample a bouncing pressed switch at the "release" state, that's fine too because it will certainly be done bouncing when you sample it again 20-msecs later at the "pressed" state. You're talking about a very decent response time ranging from 20 to 40 msecs.

And that begs the question, why bother with the "if" statement at all?

Because we do not know when the switch was pressed. If we used interrupts and the switch triggered it, we could simply wait 20ms for it to settle and then verify or assume all is good. In our case, we have 8 switches and all could have been switched at different times. For example switch 0 was pressed at t, switch 1 at t+18ms, then we latched at t+19ms, switch 0 was OK by that time but switch 1 was bouncing and we latched off instead on.

For this project it doesn't really matter because we are only displaying the value, but if it was doing something else, it could matter. I figured I will include that as an example of how I am handling de-bouncing. Also, I am testing if all switches are ready, but they can be tested individually, something you cannot easily do with interrupt/WTD method.

Share this post


Link to post
Share on other sites
11111110 (0 means switch is not ready, bouncing, switchReady & 0xFF is false)

Rob, doesn't (11111110 & 0xFF) == true (non-zero result)? The if(switchReady & 0xFF) instruction produces the same results as the if(switchReady) instruction in the PIC Simulator I'm using.

Share this post


Link to post
Share on other sites
11111110 (0 means switch is not ready, bouncing, switchReady & 0xFF is false)

Rob, doesn't (11111110 & 0xFF) == true (non-zero result)? Am I messing up my "&" and "&&" logic? In the PIC Simulator I'm using, the result is "true"...

You are correct, it should be

if(switchReady == 0xFF)

I was reusing this bit from my other project where I was masking some higher bits, only some were used for switches.

if((switchReady & 0x3F) == 0x3F)

Another good catch! :)

I am getting sloppy or just need some rest.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...