• FInal Assembly

    leumasyerrp03/04/2023 at 16:39 0 comments

    I was able with a little coaxing to stuff all the electronics back into the toy and reassemble it. With some help from my lab assistant in the photo below, assembly took about 1.5 hours.

    I started assembly by placing the vibration switch first since it was the largest of the new components. I trimmed some of the plastic supports and glued it into place on the side under the negative battery terminal. Then I soldered the input protection MOSFET to the power switch utilizing the unused 3rd pin on the switch to make a positive power rail. The PFS154-S08 was glued to the top of the battery compartment. The final component to place was the MOSFET for the motor which I also soldered to the 3rd pin on the power switch.

    With everything wired up I tested the toy to make sure it would work and then filled in some of the areas with hot glue to reduce the chance of connections working loose.

    The final step was to test fit the cover and then after confirming everything would fit I applied super glue to the seam to seal up the electronics.

    Now for some play testing and obligatory cat video.

  • Breadboarding and Code

    leumasyerrp02/27/2023 at 00:12 0 comments

    I prototyped the proposed schematic on a breadboad for testing.

    This way I was able to test the code as I wrote it. The code is below with a few notes following explaining some parts of the code. It is fully commented so that when in a few months if I have a project I want to use the PFS154-S08 for I can look back at this project as a refresher.

    /* Smart SmartyKat Crazy Cruiser
     * Target Device: PFS154-S08
     * 
     * Sam Perry 2023
     * github.com/sdp8483/Smart_SmartyKat_Crazy_Cruiser
     */
    
    #include <stdint.h>
    #include <pdk/device.h>
    #include "auto_sysclock.h"
    
    // Pin Defines - all pins are on port A
    #define VIBE_PIN              0     /* vibration sensor input pin, used to wake from deep sleep */
    #define MOTOR_PIN             4     /* motor control pin, controlled with a pmosfet */
    #define LED_PIN               3     /* LED output pin, current source */
    
    // Output Pin Fuction Defines
    #define LED_ON()              PA |= (1 << LED_PIN)
    #define LED_OFF()             PA &= ~(1 << LED_PIN)
    #define LED_TOGGLE()          PA ^= (1 << LED_PIN)
    #define MOTOR_ON()            PA &= ~(1 << MOTOR_PIN)
    #define MOTOR_OFF()           PA |= (1 << MOTOR_PIN)
    
    
    // Toggle the motor on and off to give toy some character using profiles
    #define MAX_TICKS             64    /* go to sleep after this many ticks */
    uint8_t tick = 0;                  /* tick count, number of T16 interrupts since starting T16 */
    #define NUM_PROFILES          8     /* number of profiles, a new one is played each wake event to give more character */
    uint64_t profile[NUM_PROFILES] = {0b1100110011001111111111000000000010101010101010101010111111111111,
                                      0b1111111111111111111111111111111111111111111111111111111111111111,
                                      0b1100110011001100110011001100110011111111111111111111111111111111,
                                      0b1111111111001111001111001111001111001111001111001111001111001111,
                                      0b0101010101010101010101010101010101010101010101010101010101010101,
                                      0b1111001110011100111001110011100111001110011100111001110011100111,
                                      0b1110111000000111000000000000000011111111111111111111111111111111,
                                      0b1110101010101010000000001111111101010101000000001111111101010101};
                                       /* motor will be turned on when bit is 1 and off when bit is 0 
                                           this playback profile is backwards */
    uint8_t profile_i = 0;              /* profile number to playback, increments each wake */
    
    // State Machine
    typedef enum {
      GOTO_SLEEP,                       /* prepare to sleep */
      SLEEP,                            /* toy is in deep sleep */
      WAKEUP,                           /* toy was awaken from deep sleep */
      TOCK,                             /* T16 calling for next profile point */
      LIGHT_SLEEP,                      /* light sleep between ticks */
    } fsm_states_t;
    
    volatile fsm_states_t fsm_state = GOTO_SLEEP;
    
    // Function Prototypes
    void settling_delay(void);          /* use timer3 as delay to wait for vibe sensor to settle */
    
    // Service Interrupt Requests
    void interrupt(void) __interrupt(0) {
      /* Some notes and thoughts about interrupts on the PFS154
       *  Section 5.7 of the datasheet contains information about the interrupt controller.
       *  When an interrupt is triggered global interrupts are disabled, ie __disgint() is automaticaly called.
       *  CPU steps into ISR function below and executes code there. When done __engint() is automaticaly called and 
       *  code execution starts in the main loop where it left off. Confusingly the datasheet says that even if INTEN = 0 
       *  INTRQ can still be triggered by the interrupt source. So the peripheral or port should be further disabled to prevent
       *  triggering. */
    
      if (INTRQ & INTRQ_PA0) {          /* wake pin was pulled low */
        INTRQ &= ~INTRQ_PA0;            /* mark PA0 interrupt request serviced */
        fsm_state = WAKEUP;             /* change state */
      }
    
      if (INTRQ & INTRQ_T16) {          /* timer has expired */
        INTRQ &= ~INTRQ_T16;            /* mark T16 interrupt request serviced */
        T16C = 0;                       /* reset timer to zero */
        fsm_state = TOCK;               /* get next profile point */
      }
    
      if (INTRQ & INTRQ_TM2) {          /* LED toggle timer */
        INTRQ &= ~INTRQ_TM2;            /* mark interrupt request serviced */
        fsm_state = LIGHT_SLEEP;        /* go to light sleep */
      }
    
      if (INTRQ & INTRQ_TM3) {          /* settling delay has expired */
        INTRQ &= ~INTRQ_TM3;            /* mark interrupt request serviced */
      }
    }
    
    // Main program
    void main() {
      MISC |= MISC_FAST_WAKEUP_ENABLE;  /* enable faster wakeup, 45 ILRC clocks...
    Read more »

  • Go To (Deep) Sleep!

    leumasyerrp02/25/2023 at 12:43 0 comments

    I wrote some code for the PFS154-S08 using interrupts to wake the uC using the vibration switch, turn on the LED and motor for a few seconds and then turn everything off and go into deep sleep. This was simple code to make sure my code structure would work and then I would build on it for more complicated features such as blinking the LED and PWMing the motor. Everything was going just fine with the vibration switch waking up the uC and the LED and motor turning on and off as expected. The only issue I found that was when the uC was set to deep sleep it was drawing around 50uA.

    The datasheet for the PFS154 claims a typical deep sleep using stopsys should consume around 0.5uA (pg. 16). I suspected that for some reason the uC was not going into deep sleep so I wrote some super simple code that setup the uC similar to how I had it setup but went into stopsys before reaching the forever loop. The current draw was still higher then the datasheet. I wanted the uC to go to sleep and I suspected that it was not.

    To prove to myself that it was not going into sleep I wrote the following code that toggles a PIN in a while loop after a call to stopsys. Since I don't have any wake pins enabled the uC should never reach the pin toggling code.

    /* Go To (Deep) Sleep! V1
     *  testing the deep sleep of the PFS154-S08
     *  compiled using free-pdk
     */ 
     
    #include <stdint.h>
    #include <pdk/device.h>
    #include "auto_sysclock.h"
    
    // Pin Defines - all pins are on port A
    #define PIN   3
    
    // Output Pin Function Defines
    #define PIN_TOGGLE()  PA ^= (1 << PIN)
    
    // Main Program
    void main() {
      MISC |= MISC_FAST_WAKEUP_ENABLE;  /* enable faster wakeup, 45 ILRC clocks instead of 3000 */
    
      PADIER = 0;                       /* on reset all pins are set as wake pins, 
                                           setting register to 0 to disable */
    
      PAC |= (1 << PIN);                /* set pin as output */
    
      __stopsys();                      /* go to deep sleep */
    
      // forever loop - code execution should never reach this
      while(1) {
        PIN_TOGGLE();
        
        // simple delay
        for (int16_t i=0; i<1000; i++) {
          __nop();
        }
      }
    }
    
    // Startup code - Setup/calibrate system clock
    unsigned char _sdcc_external_startup(void) {
      /* Set the system clock 
       * note it is necessary to enable IHRC clock while updating clock settings or CPU will hang  */
      PDK_USE_ILRC_SYSCLOCK();          /* use ILRC 55kHz clock as sysclock */
      PDK_DISABLE_IHRC();               /* disable IHRC to save power */
      EASY_PDK_CALIBRATE_ILRC(F_CPU, TARGET_VDD_MV);
    
      return 0;   // Return 0 to inform SDCC to continue with normal initialization.
    }

    To my surprise when I probed PA3 it was toggling and my current draw was again ~50uA. Why wont you go to sleep!

    When I first started playing around with the PFS154-S08 I found a project for an Ultra Low Power LED Flasher using the PFS154. I remembered that in that code the author also disabled the wake function on port B since it would cause the uC to wake even though there is no port B on the -S08 variant of the PFS154. So with the modified code below I was finally able to get the uC to go to (deep) sleep with a current draw of around 0.3uA at Vdd=3.0V.

    /* Go To (Deep) Sleep! V2
     *  testing the deep sleep of the PFS154-S08
     *  compiled using free-pdk
     */ 
     
    #include <stdint.h>
    #include <pdk/device.h>
    #include "auto_sysclock.h"
    
    // Pin Defines - all pins are on port A
    #define PIN   3
    
    // Output Pin Function Defines
    #define PIN_TOGGLE()  PA ^= (1 << PIN)
    
    // Main Program
    void main() {
      MISC |= MISC_FAST_WAKEUP_ENABLE;  /* enable faster wakeup, 45 ILRC clocks instead of 3000 */
    
      PADIER = 0;                       /* on reset all pins are set as wake pins, 
                                           setting register to 0 to disable */
      PBDIER = 0;                       /* there is no port B on the -S08 package, 
                                           without setting this to 0 the uC will wake unexpectedly */
    
      PAC |= (1 << PIN);                /* set pin as output */
    
      __stopsys();                      /* go to deep sleep */
    
      // forever loop - code execution should never reach this
      while(1) {
        PIN_TOGGLE();
        
        // simple delay
        for (int16_t i=0; i<1000; i++) {
          __nop();
        }
      }
    }
    
    // Startup code - Setup/calibrate system clock
    unsigned char...
    Read more »

  • Proposed Schematic

    leumasyerrp02/06/2023 at 00:10 0 comments

    Nothing complicated about the schematic. A few things to point out about why I chose the parts I did.

    • First I am using only components that I have on hand, I did purchase the SW-18001P vibration switch but everything else is what I can find in my stash or is reused from the original toy components.
    • I use a P-channel MOSFET for reverse polarity protection because it introduces the lowest voltage drop. A shottky diode would be about 0.2V. The datasheet for the NCE3401 does not directly show what the Rdson at currents lower the 1A would be but extrapolating it looks to be around 75mΩ. At the maximum current draw of this circuit of 51mA that would be a voltage drop of 3.8mV. I will measure this when I build up a circuit to confirm.
    • To reduce BOM count I am using the same P-channel MOSFET that is used for reverse polarity protection to also switch the rumble motor. I could just as easily use a N-channel or NPN transistor.
    • I have been playing around with the Padauk PFS154-S08 for the last few weeks so I would like to use it in a project. This microcontroller will run from 2.0V~5.5V so it covers the usable range of the LR44 batteries. This model of the famous 3¢ microcontroller is multi-programmable (so double the cost, big spender here).

    Now I am off to programming and building this up on a breadboard.

  • Current Measurements and Getting Inside

    leumasyerrp02/04/2023 at 19:39 0 comments

    Before improving the battery life of this toy I wanted to see what the current draw was of the not so smart version. Under battery power I measured 35mA with the batteries at around 2.47V.

    To get a current curve at different voltages it was easier to open up the toy so that I could clip on test
    leads. The plastic housing is glued together at the seam. A small vise made quick work of opening this up.

    I measured the following current draw at voltages from 2V to 3V.

    Voltage [Volts]
    Current [mA]
    3.051.0
    2.844.1
    2.638.8
    2.432.1
    2.226.5
    2.021.0