Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by RobG

  1. Thanks. RTP = Research Triangle Park in North Carolina.
  2. OK, I know I am not new here, I've been here for over a month now, but I thought I will introduce myself. I am a Java developer, Apple die hard, playing with electronics for the past quarter of a century. My first computer was ZX Spectrum (known here in US as Timex Sinclair 2068,) then all bunch of others, but never PC. Msp430 is not my first attempt at uCs. Wanted to learn 68HC, got few things going on PICs, but what TI did with LaunchPad is really what I needed, kudos TI. I have many ideas and no time. Rob (Cary, NC)
  3. My pleasure. The next logical step: IR receiver (I was actually thinking to do a complete IR RC solution using 2 LaunchPads, time permitting.)
  4. Sure, in this case we can do common collector.
  5. The code is now final (I am still waiting for some parts and as soon as I get them I will add a video.)
  6. RobG

    I2C problems

    @displacedtexan Did you look at the I2C examples included in code examples?
  7. Nice, very good suggestions, I knew there was an easier way to do it, just couldn't see it. The simplest solutions are always in front of us. This is the reason I posted before before this thing was finished. Thanks for the feedback!
  8. Are there any codes that do work? Like half price codes?
  9. RobG

    I2C problems

    The address is 7bit and it's stored in bit1-bit7, bit0 indicates read or write (it is really weird that they are using upper bits to store address and bit0 to indicate R/W, but this thing was designed in 80s so maybe there was a reason for it, like hardware design, for example if you want to expand to 15bits, all you do is add another byte on top and your R/W bit stays in the same place.) In datasheet, they are basically simplifying this, instead of saying that address is 0x77 and make you shift left with borrow 1 for read or 0 for write, they are giving you 8bit values for read and write. 0x77 address might be confusing and some might be tempted to clear bit0 for write without shifting it first.
  10. I am in the process of creating my first entry to the project of the month contest, IR remote control for Sony devices. There are 3 switches, Volume Down, Volume Up, and Power, which are connected to ports P1.1 through P1.3. P1.4 will drive the transistor through resistor. MCU is in LPM4 mode waiting for a button to be pressed. Once the button is pressed, the mode is changed to LPM0 so the Timer can "work" on sending the command. After the command is sent, we check the button and start another cycle or change back to LPM4 mode. EDIT: I've made some changes as suggested, we do not need P1.0 anymore, we will simply change the function of P1.4 from I/O (no carrier) to SMCLK out (carrier on.) #include "msp430g2231.h" #define T600us 23 // 600us #define T1200us (T600us * 2) // 1200us #define T2400us (T1200us * 2) // 2400us #define T24ms (T2400us * 10) // 24ms #define BIT1_3 (BIT1 + BIT2 + BIT3) // all 3 bits, those will be used for switches unsigned int counter = 0; // main counter unsigned char command = 2; // current command unsigned char bitCounter = 0; // command's bit counter unsigned char carrierFlag = 0; // flag to indicate carrier on or off unsigned int tmpCommand = 0; // copy of the current command used for shifting // Sony commands: 7 bit Command + 5 bit Address, bit0 is MSB // Volume - 19 0000 (pad to 16) + 1100100 (command) + 10000 (device Address: TV) = 0x0C90 // Volume + 18 0000 + 0100100 + 10000 = 0x0490 // Power 21 0000 + 1010100 + 10000 = 0x0A90 unsigned int commands[3] = { 0x0C90, 0x0490, 0x0A90 }; // list of available commands 0 Volume Down, 1 Volume Up, 2 Power void main(void) { WDTCTL = WDTPW + WDTHOLD; // stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; // as above BCSCTL1 &= ~(RSEL2 + RSEL3); // as above BCSCTL2 |= DIVS0 + DIVS1; // divide SMCLK by 8 which will give us ~38kHz P1DIR |= BIT4; // port 1.4 is configured as SMCLK out and connected to IR emitter, this will be our carrier frequency, 38kHz P1OUT &= ~BIT4; P1SEL &= ~BIT4; // for now turn it off P1REN |= BIT1_3; // P1.1-P1.3 pull-up P1OUT |= BIT1_3; // P1.1-P1.3 pull-up P1IE |= BIT1_3; // P1.1-P1.3 interrupt enabled P1IES |= BIT1_3; // P1.1-P1.3 hi/lo edge P1IFG &= ~BIT1_3; // P1.1-P1.3 IFG cleared TACCR0 = T600us; // interrupt every 600us for starters TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, upmode tmpCommand = commands[command]; // default cmd __bis_SR_register(LPM0_bits + GIE); // switch to LPM0 with interrupts } // Timer A interrupt service routine #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if(counter == 0) { // start with start bit P1SEL |= BIT4; // carrier on TACCR0 = T2400us; // start bit is on for 2400us } else { if(bitCounter < 12) { // we have 12 bits to process if(!carrierFlag) { P1SEL &= ~BIT4; // carrier off, once before every bit TACCR0 = T600us; // no carrier for 600us } else { P1SEL |= BIT4; // carrier on if(tmpCommand & 0x0800) { // test bit 12, since we are only using 12 out of 16 bits TACCR0 = T1200us; // if bit is 1, carrier on for 1200us } else { TACCR0 = T600us; // if bit is 0, carrier on for 600us } bitCounter++; tmpCommand<<=1; // shift bits to the left in tmp variable } carrierFlag ^= 1; // toggle flag } } counter++; if(counter == 26){ // we are done sending current frame P1SEL &= ~BIT4; // carrier off counter = 0; // reset all counters bitCounter = 0; carrierFlag = 0; if(~P1IN & BIT1_3) { // check if the button is still pressed tmpCommand = commands[command]; // still pressed, repeat last command TACCR0 = T24ms; // delay, frames should be ~45ms apart } else { // we are done, go back to sleep P1IE |= BIT1_3; // enable P1.1-P1.3 interrupt TACCTL0 &= ~CCIE; // CCR0 interrupt disabled __bis_SR_register_on_exit(LPM4_bits); // switch from LPM0 to LPM4 } } } // Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) { P1IE &= ~BIT1_3; // disable P1.1-P1.3 interrupt P1IFG &= ~BIT1_3; // P1.1-P1.3 IFG cleared if(~P1IN & BIT1) { // check which switch was pressed, set command command = 0; } else if(~P1IN & BIT2) { command = 1; } else if(~P1IN & BIT3) { command = 2; } else { // nothing pressed? P1IE |= BIT1_3; // enable P1.1-P1.3 interrupt return; // return to LPM4, keep timer off } tmpCommand = commands[command]; TACCTL0 = CCIE; // CCR0 interrupt enabled __bic_SR_register_on_exit(LPM4_bits-LPM0_bits); // switch from LPM4 to LPM0 }
  11. I am working on optimizing that code so multiple commands can be used. When it comes to range and transistor, you can do something like this:
  12. Maybe you should start with something easier, like Java. You cannot use it for MSP430 and many things about that language are different, but you will get an idea what programming is all about. And look for C tutorials, not C++.
  13. RobG

    PWM codes and TimerA

    toggle ^= 1 expression is equivalent to toggle = toggle ^ 1 ( ^ is XOR), it's basically a flip-flop. When toggle is 0, expression evaluates to 1 and if(toggle ^= 1) executes true block 0 XOR 1 = 1 Next time around, toggle is 1, expression evaluates to 0 and if(toggle ^= 1) executes false block 1 XOR 1 = 0
  14. Here's working example using the code above, TV Volume Down command is sent over and over:
  15. Should be 480 cycles, 0.00006 * 8000000 Also, if you decide to use delay solution, you can lower clock frequency or simply divide it. What I would do is find the smallest interval needed, and set the timer to interrupt at that interval. So if you need 60us and you are using default 8MHz clock with divide by 8, then your timer will run at 1MHz, which equals to 1us. Then set timer to up mode, enable CCR0 interrupt, and set CCR0 to 60. You will end up in your interrupt handler every 60us and all you need to do is track what needs to be done in the current cycle using vars. When you loop, CPU is running for no reason, when you use the above method, CPU is sleeping most of the time preserving your battery. BTW, I am looking forward to your final product as I have few of those but no time to experiment with them. #include unsigned int stepNum = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD;
  16. Thanks for the info! I almost bought one few days ago at the full price, now I got it for free (s/h was free too!) Looks like TI really wants to boost their market share, good for us developers.
  17. Personally, I am not a big fan of delays. I would use interrupts for timing purposes, especially if you are looking for low power consumption. I have few examples in projects section, but if you need more help let me know.
  18. Sorry for the confusing image, the bottom part is the output from IR decoder, which is low when carrier is present. Anyway, following is a new code, which will basically send one frame over and over. It's a simplified version, frame is represented by array of 0's and 1's to make things clearer. There's still no switch logic. When completed, this is how it's going to work: after setting up clock, in, out, and timer, we will put MCU to sleep. Input interrupt will enable timer int. and disable input int. Timer will be then used to send the frame and after it's done, will disable timer int., enable input int., and will put MCU to sleep. EDIT: corrected couple of problems with the code, replaced s1Frame with the real code. #include "msp430g2231.h" unsigned int counter = 0; // This is code 19, address 1 - TV Volume Down unsigned char s1Frame[40] = {1,1,1,1, // Start - 4x600us 0,1,1, // 1 - no carrier for 600us, then carrier for 1200us 0,1,1, // 1 0,1, // 0 - no carrier for 600us, then carrier for 600us 0,1, // 0 0,1,1, // 1 0,1, // 0 0,1, // 0 0,1,1, // 1 0,1, // 0 0,1, // 0 0,1, // 0 0,1, // 0 0,0,0,0,0,0,0,0}; // Pad to 40, which is max if all were 1's, (0,1,1) void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT DCOCTL |= DCO0 + DCO1; // DCO = ~300kHz BCSCTL1 |= RSEL0 + RSEL1; // as above BCSCTL1 &= ~(RSEL2 + RSEL3); // as above BCSCTL2 |= DIVS0 + DIVS1; // divide SMCLK by 8 which will give us ~38kHz P1DIR |= BIT0; // Port 1.0 to IR emitter via resistor P1OUT &= ~BIT0; // This will be our data out P1DIR |= BIT4; // Port 1.4 is configured as SMCLK out and connected to IR emitter P1SEL |= BIT4; // This will be our carrier frequency, 38kHz TACCTL0 = CCIE; // CCR0 interrupt enabled TACCR0 = 23; // interrupt every 600us TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, upmode __bis_SR_register(GIE); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if(counter > 39) { P1OUT &= ~BIT0; // Delay for ~24ms. Frames should be spaced 45ms apart if I am not mistaken } else { if(s1Frame[counter]) { P1OUT |= BIT0; // carrier on } else { P1OUT &= ~BIT0; // carrier off } } counter++; if(counter == 80) { counter = 0; // We are done, we can go back to sleep or start new frame } }
  19. I am working on a project which is closely related to this post and I figured I will add my $0.02. The code below has only carrier and some data part. Missing is the switch handling and actual message content part. The way it works, master clock uses DCO which is set to ~300kHz, sub clock is master divided by 8, which gives us ~38kHz, perfect for carrier frequency. We connect IR emitter to P1.4, SMCLK out, to modulate our data. CCR0 generates interrupt every 600us, which we then can use to generate actual message and drive P1.0. #include "msp430g2231.h" void main(void) {
  20. RobG

    PWM codes and TimerA

    Your problem is that you are mixing two timers, TimerA and TimerB. I don't have MCU you are using, so I show you the code for g2231 that does what you need. #include "msp430g2231.h" unsigned int toggle = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1DIR |= BIT2; P1SEL |= BIT2; // P1.2 TA1 option select TACCTL0 = CCIE; TACCR0 = 1000; TACCTL1 = OUTMOD_7; TACCR1 = 500; TACTL = TASSEL_2 + MC_1 + ID_0; __bis_SR_register(GIE); } #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A (void) { if(toggle ^= 1) { TACCR1 = 500; } else { TACCR1 = 400; } } Your code should look something like this: #include "msp430x22x4.h" unsigned int toggle = 0; void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P4DIR |= 0x20; // P4.5 output P4SEL |= 0x20; // P4.5 TBx options TBCCTL0 = CCIE; TBCCR0 = 1000; // PWM Period TBCCTL2 = OUTMOD_7; // TBCCR2 reset/set TBCCR2 = 500; // TBCCR2 PWM duty cycle, 50% TBCTL = TBSSEL_2 + MC_1; // SMCLK, up mode __bis_SR_register(GIE); } #pragma vector=TIMERB0_VECTOR __interrupt void Timer_B (void) { if(toggle ^= 1) { TBCCR2 = 500; } else { TBCCR2 = 400; } }
  21. I was thinking that not many of us will ever need to use 8 servos so I have updated my code and here it is. The new version controls up to 4 servos and does not require additional parts. Just connect your servo to pins P1.4-P1.7 and voila. #include unsigned int counter = 0;
  22. RobG


    Logic analyzer? Something simple, maybe 4 inputs, or 8 using external shift register.
  23. That would be the case if we fed latch (storage clock) with inverted shift clock (register clock) as per specs.
  24. RobG

    PWM codes and TimerA

    TBCCR0 = 512 - 1; // PWM Period TBCCTL2 = OUTMOD_7; // TBCCR2 reset/set TBCTL = TBSSEL_2 + MC_1; // SMCLK, up mode the above lines are redundant in your interrupt routine (plus they only need to be executed once) and I am not sure but they may affect the timer. I would remove them from interrupt and put the just before __bis_SR...
  • Create New...