Jump to content
roadrunner84

Nano project: fix an IKEA toy

Recommended Posts

So we have this IKEA toy kitchen (Duktig). There are two hotplates in it (LED lit plastic) which run from 6xAA cells. Within two weeks these cells are drained! So I fixed it :)

With an MSP430, this is for a 14-pin value line, but it could run on a 20-pin value line, or even on an 8-pin value line (MSP430G22x0).

It's really straighforward, except for the _BIC_SR_IRQ

#include <msp430.h>

#define P2MAP (BIT6 | BIT7) /* set to 0xFF for 20-pin device */
#define TOP_BUTTON    BIT0
#define BOTTOM_BUTTON BIT1
#define TOP_PLATE     BIT2
#define BOTTOM_PLATE  BIT3

int main(void)
{
  // Stop WDT
  WDTCTL = WDTPW | WDTHOLD; // Stop WDT
  BCSCTL1 = DIVA_3; // ACLK /= 8
  BCSCTL2 = SELM_3 | SELS; // MCLK = VLO, SMCLK = VLO
  BCSCTL3 = LFXT1S_2; // LFXT1 = VLO

  // Set up I/O (buttons as interrupt, LEDs off)
  P2SEL = 0; // Set crystal I/O to GPIO
  P1REN = ~(TOP_PLATE | BOTTOM_PLATE); // All pin pull up/down, except ouputs
  P2REN = P2MAP; // Also on port 2
  P1OUT = TOP_BUTTON | BOTTOM_BUTTON; // pull up button inputs
  P1DIR = ~(TOP_BUTTON | BOTTOM_BUTTON); // disable all input, except inputs
  P2DIR = P2MAP; // Also on port 2
  P1IES = TOP_BUTTON | BOTTOM_BUTTON; // high->low transition
  P1IFG = 0; // clear interrupt flags
  P1IE = TOP_BUTTON | BOTTOM_BUTTON; // enable interrupts

  LPM4;
}

#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
  IE1 &= ~ WDTIFG; // clear WDT interrupt flag
  WDTCTL = WDT_ARST_1000; // WDT on at ACLK/32k => ~22 sec

  // Toggle respective LED
  if (P1IFG & TOP_BUTTON)
  {
    P1IFG &= ~TOP_BUTTON;
    P1OUT ^= TOP_PLATE;
  }
  if (P1IFG & BOTTOM_BUTTON)
  {
    P1IFG &= ~BOTTOM_BUTTON;
    P1OUT ^= BOTTOM_PLATE;
  }

  // Leave LPM4
  _BIC_SR_IRQ(OSCOFF); // Enable ACLK after ISR (LPM4 -> LPM3)
}
Nowhere are we limited to leave low power mode altogether. So instead of leaving low power (4) mode and entering LPM3 again from the main, I just alter the difference between LPM4 and LPM3 and so enable ACLK.

When imported in Energia this compiles to 241 bytes.

 

The idea is that when toggling a hotplate on or off, the timer will start (or restart). I used the WDT because it's easy; it will reset the chip for me. After the timeout occurs, main() is executed again and will put the chip in LPM4 again.

duktig-mini-kitchen__0086283_PE214923_S4

Share this post


Link to post
Share on other sites

Cool, we have the same one :smile: Our kids accepted that the plates are not working after the 2nd charge  :?

I never checked what's inside (which is actually odd for me ;-))

Did you rip out the original stuff? Does that run on 3V?

Aren't the 22secs a bit short?

 

Just two minors on your code:

I guess this should be BCSCTL2 = SELM_3 | SELS and there is a typo in the __interrupt


 

Share this post


Link to post
Share on other sites

This would have given me a hard time sleeping tonight. Kids are in bed, so I opend it up. I guess the PCB in there will be gone this weekend ;-)

Thanks for posting this "nano project"!

 

post-30789-136148631625_thumb.jpg

Share this post


Link to post
Share on other sites

I've opened mine up too. And pulled the PCB out to photograph the backside. Give me a few minutes to "reverse engineer" it.

So far I noticed it's running from a 4MHz crystal and uses two transistors for LED driving. The only resistor type used is 1kOhm~1%.

The buttons, batteries and LEDs are connected to the PCB using latching connectors. This could save Ikea a bunch of money if they just solder the stuff right on there!

Share this post


Link to post
Share on other sites

WGaT6QY.png

Thanks to digikey for their drawing tool (singce I have no EDA at hand).

This is the "guts" of the hotplates. The chip (the 14-pin header in the middle of this drawing) is designated "XH8815A", of which I find no information at all.

So the switches use pull up. There is one weird capacitor between pin 7 of the chip and Vcc, maybe it's reset related, but I'm not sure (who knows?). It looks like the Vcc is at 6 volts, while Vdd is at 9 volts. I assumed the transistors are of the NPN type, since they have one pin connected to ground.

This seems really straightforward and easy to replace. We're just out of luck with the pinning; all value line MSP430 have RST and TEST next to the power supply, so we'll need to alter the circuit to have a drop in replacement. The crystal is kind of useless for the MSP, since we have this great VLO available.

Share this post


Link to post
Share on other sites

Drop in ready :smile:

RfCWFsG.png

A small side note:

I used a resistor divider as power control (get 2.8-3.6V out of a 6V source). It's ugly and icky. If you decide to move the power tap back two notches (and get 3V instead of 6V) you can cut off R2, C2 and R3 from the right of the PCB.

This is a flip chip design; the MSP430G2210 (the second cheapest MSP430) is upside down in the cavity in the middle. The arrow points to pin number 1.

If anyone is interested in this, I can post the gerbers or the KiCad schematics if you want.

In earlier code use this

#define TOP_BUTTON    BIT2
#define BOTTOM_BUTTON BIT5
#define TOP_PLATE     BIT6 /*or is it BIT7?*/
#define BOTTOM_PLATE  BIT7 /*or is it BIT6?*/

Share this post


Link to post
Share on other sites

Awesome, that was fast! I was just looking at the schematic you posted earlier, there are just a couple of blue-wire changes and the original PCB should work with the 14pin.
However I like seeing that you used the 8pin :smile:
Since it

post-30789-0-16676300-1361542651_thumb.png

Share this post


Link to post
Share on other sites

I think I was too lazy to make a board today ;-) Just rewired the original board and used a MSP430F2011.

I added a socket, so I could save the in circuit debug and just gave it a trial on the Launchpad. To get a reasonable VCC for the 430 I changed the tap to 2 cells.

My code switches off the plates ~10Minutes after switching them on.

I'll have the kids test it tomorrow ;-)

 

This was really a great idea to put a 430 in there. I didn't think of it being that easy. Thanks!

 

post-30789-0-40446900-1361571648_thumb.jpg post-30789-0-49628300-1361571665_thumb.jpg post-30789-0-54868900-1361571688_thumb.jpg

Share this post


Link to post
Share on other sites

Nice fit, very quick too! I think the original behaviour was way less than 10 minutes, so you'll probably burn your batteries quite fast.

I see you didn't alter the resistors around the transistors, did it illuminate just as well?

I also see you skipped out on the capacitor on the reset pin, is that stable enough?

Share this post


Link to post
Share on other sites

It works perfekt, even though the LEDs seem much brighter now. Do you still have the original setup? I'd be curious if there was a PWM on the LEDs. Now I measured ~17mA per plate.

Guess you are right on the time. I didn't know it switched off automatically before. I always thought the kids left it on all the time, that's why the batteries died so fast. Probably a minute would do just as good.

At least the huge standby current of the original is gone :)

The reset is OK in this minimalistic approach, if it would go to production I'd recommend to use it ;)

 

Share this post


Link to post
Share on other sites

I would measure the signals coming out of the old chip if I'd have a scope lying around :(

I also would like to measure current before opening up the thing, so I can do some benchmarking on how much better it has become.

I might be able to do some measurements next friday...

Did you use the code I provided, or did you modify it, or made your own? Since my current code does only allow for 22second timeout, I might change it into a software WDT and reset after X counts.

Did you do button debouncing in software? I found this article on debouncing buttons, quite interesting.

Share this post


Link to post
Share on other sites

I got the XH8815A back working on a breadboard. It does PWM the LED outputs indeed.

 

post-30789-0-18455000-1361644187_thumb.jpg

 

I measured the current and it's ~2mA @ 5V in idle and when the ports are on.  :shock:

Not a big surprise the batteries drained that quickly.  :?

 

I made a new code, it's below. The debounce is just a short loop after the ISR gets called.

I'll change my code to have the PWM too. The LEDs are way too bright now and the current is also way too high.

 

#include <msp430f2012.h>

#define LED1 BIT4
#define LED2 BIT5
#define KEY1 BIT7
#define KEY2 BIT6

unsigned int time;

#define Version 0x0100
#define VersionLocation 0x0FFFF-RESET_VECTOR-3
__root static const int x@VersionLocation = Version;

int main(void)
{
        WDTCTL = WDTPW + WDTHOLD;

        P1OUT = 0;
        P1DIR = BIT0+BIT1+BIT2+BIT3+LED1+LED2;
        P1IES = KEY1+KEY2;
        P1IFG = 0;
        P1IE = KEY1+KEY2;
        P2SEL = 0;
        P2REN = BIT6+BIT7;

        if(CALBC1_1MHZ != 0xFF)
        {
        BCSCTL1 = 0;
        BCSCTL1 = CALBC1_1MHZ;
        DCOCTL = CALDCO_1MHZ;
        }
        BCSCTL3 |= LFXT1S_2; //VLO 2 ACLK

        IFG1 &= ~WDTIFG;
        IE1 |= WDTIE;
        __enable_interrupt();

        while(1)
        {
        __low_power_mode_4();
        }
}

/******************************************************************************/
// Interrupt Routines
/******************************************************************************/

#pragma vector=WDT_VECTOR
__interrupt void ISR_WDT(void)
{
        IFG1 &= ~WDTIFG;              // Clear WDT interrupt flag
        time++;
        if(time>=200)                 // Actual seconds is roughly time x 3
        {
        time=0;
        _BIS_SR_IRQ(OSCOFF);
        WDTCTL = WDTPW + WDTHOLD;
        P1OUT &= ~(LED1+LED2);
        }

}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
        for(unsigned int i=0;i<2;i++){for(unsigned int j=0;j<0xFFFF;j++){}}  //Debounce
        if(P1IFG == KEY1)
        {
        P1OUT ^= LED1;
        }
        else
        {
        P1OUT ^= LED2;
        }
        P1IFG = 0;

        if(P1OUT == LED1 | LED2)
        {
        WDTCTL = WDTPW + WDTTMSEL + WDTCNTCL + WDTSSEL; // watchdog counter mode, ACLK, /32768
        _BIC_SR_IRQ(OSCOFF);
        }
}

 

 

Share this post


Link to post
Share on other sites

10.23ms sounds a lot like a 10 bit timer :D

The 30% duty cycle sounds weird though.

What's that magic you're preforming with Version location? It looks like some kind of bootloader thing.

One comment though; you should set time to 0 when the port1 interrupt is serviced as well, otherwise turning a plate on, then off, wait 50 seconds, turn on would turn the plates off in less then 10 seconds.

Share this post


Link to post
Share on other sites

Think I got it, looks much better now, no sunglasses required any more when looking at the plates ;-)

Good point with the time, I added it to the P1 ISR.

Small drawback of the bit-banged PWM is that it gets interrupted when pushing the other button.

But rewiring to the timer outputs is too much change on the original board, but might make sense if you do a new PCB.

 

The VersionLocation is just a version marker I like to be placed one work above the interrupt vectors. I do it in all my codes. Once in a while it comes in handy when downloading the code from a unit later.

 

That's a memory dump for example:

FFD0 FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0101
FFE0 FFFF FFFF E4F8 FFFF FFFF FFFF FFFF FFFF
FFF0 FFFF FFFF 64F9 FFFF FFFF FFFF FFFF 00F8

 

#include <msp430f2012.h>

#define LED1 BIT4
#define LED2 BIT5
#define KEY1 BIT7
#define KEY2 BIT6
#define ON   1
#define OFF  0

unsigned char LEDstate1;
unsigned char LEDstate2;
unsigned int time;

#define Version 0x0101
#define VersionLocation 0x0FFFF-RESET_VECTOR-3
__root static const int x@VersionLocation = Version;

int main(void) {
	WDTCTL = WDTPW + WDTHOLD;

        P1OUT = 0;
        P1DIR = BIT0+BIT1+BIT2+BIT3+LED1+LED2;
        P1IES = KEY1+KEY2;
        P1IFG = 0;
        P1IE = KEY1+KEY2;
        P2SEL = 0;
        P2REN = BIT6+BIT7;

        if(CALBC1_1MHZ != 0xFF)
        {
        BCSCTL1 = 0;
        BCSCTL1 = CALBC1_1MHZ;
        DCOCTL = CALDCO_1MHZ;
        }
        BCSCTL3 |= LFXT1S_2; //VLO 2 ACLK

        IFG1 &= ~WDTIFG;
        IE1 |= WDTIE;
        __enable_interrupt();

        while(1)
        {
        __low_power_mode_4();
        __no_operation();
            while(1)
            {
              if(LEDstate1 == ON)
              {
                P1OUT |= LED1;
              }
              if(LEDstate2 == ON)
              {
                P1OUT |= LED2;
              }
              for(unsigned int j=0;j<0x250;j++){}
              if(LEDstate1 == ON)
              {
                P1OUT &= ~LED1;
              }
              if(LEDstate2 == ON)
              {
                P1OUT &= ~LED2;
              }
              for(unsigned int j=0;j<0x500;j++){}
              if((time>=100) || ((LEDstate1 == OFF) && (LEDstate2 == OFF)))                 // Actual seconds is roughly time x 3
              {
              time=0;
              WDTCTL = WDTPW + WDTHOLD;
              LEDstate1 = OFF;
              LEDstate2 = OFF;
              P1OUT &= ~(LED1+LED2);
              break;
              }
            }
        }
}

/******************************************************************************/
// Interrupt Routines
/******************************************************************************/

#pragma vector=WDT_VECTOR
__interrupt void ISR_WDT(void)
{
        IFG1 &= ~WDTIFG;              // Clear WDT interrupt flag
        time++;
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
        for(unsigned int i=0;i<2;i++){for(unsigned int j=0;j<0xFFFF;j++){}}  //Debounce
        if(P1IFG & KEY1)
        {
        LEDstate1 ^= 1;
        P1OUT &= ~LED1;
        }
        if(P1IFG & KEY2)
        {
        LEDstate2 ^= 1;
        P1OUT &= ~LED2;
        }
        P1IFG = 0;
        time=0;
        if(1 == LEDstate1 | LEDstate2)
        {
        WDTCTL = WDTPW + WDTTMSEL + WDTCNTCL + WDTSSEL; // watchdog counter mode, ACLK, /32768
        _BIC_SR_IRQ(OSCOFF+CPUOFF+SCG1+SCG0);
        }
}

 

 

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...