RobG 1,892 Posted December 7, 2010 Share Posted December 7, 2010 **************** 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; } tingo, pine and sirri 3 Quote Link to post Share on other sites
GeekDoc 226 Posted December 7, 2010 Share Posted December 7, 2010 Nice work. Could have been in your first thread for continuity, though. Quote Link to post Share on other sites
bluehash 1,581 Posted December 8, 2010 Share Posted December 8, 2010 Maybe, rename the topic a little bit, so that people know your controlling 7 segments with a shift register. Keep up the work! Quote Link to post Share on other sites
RobG 1,892 Posted December 20, 2010 Author Share Posted December 20, 2010 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. Quote Link to post Share on other sites
RobG 1,892 Posted January 18, 2011 Author Share Posted January 18, 2011 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; } pine 1 Quote Link to post Share on other sites
Mac 67 Posted January 26, 2011 Share Posted January 26, 2011 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 Quote Link to post Share on other sites
RobG 1,892 Posted January 26, 2011 Author Share Posted January 26, 2011 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 Quote Link to post Share on other sites
bluehash 1,581 Posted January 26, 2011 Share Posted January 26, 2011 Rob K1WAW Nice! a HAM? Quote Link to post Share on other sites
RobG 1,892 Posted January 26, 2011 Author Share Posted January 26, 2011 Yep, not active at the moment, too many responsibilities Quote Link to post Share on other sites
Mac 67 Posted January 26, 2011 Share Posted January 26, 2011 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 Quote Link to post Share on other sites
RobG 1,892 Posted January 26, 2011 Author Share Posted January 26, 2011 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. Quote Link to post Share on other sites
Mac 67 Posted January 27, 2011 Share Posted January 27, 2011 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 RobG 1 Quote Link to post Share on other sites
RobG 1,892 Posted January 27, 2011 Author Share Posted January 27, 2011 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. Quote Link to post Share on other sites
Mac 67 Posted January 27, 2011 Share Posted January 27, 2011 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. RobG 1 Quote Link to post Share on other sites
RobG 1,892 Posted January 27, 2011 Author Share Posted January 27, 2011 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. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.