Making the SmartyKat Crazy Cruiser smart to lower battery usage.
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
smart_smartyKat_crazy_cruiser.pdfAdobe Portable Document Format - 26.76 kB - 03/04/2023 at 16:41 |
|
|
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.
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 »
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 »
Nothing complicated about the schematic. A few things to point out about why I chose the parts I did.
Now I am off to programming and building this up on a breadboard.
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.0 | 51.0 |
2.8 | 44.1 |
2.6 | 38.8 |
2.4 | 32.1 |
2.2 | 26.5 |
2.0 | 21.0 |
Create an account to leave a comment. Already have an account? Log In.
This is very interesting, but where can I find a compiler/development environment and programmer for the PFS154-S08 ?
Thanks.
Do a search for Padauk as a topic on Github and the epic Padauk reverse engineering thread on EEVblog
Check out the Free PDK Documentation at https://free-pdk.github.io/
I would suggest going to a different MCU such as the ATTINY series since the programmers are super cheap and readily available. I was using what I had on hand and the MCU that I had been playing around with. The project is meant to inspire and not be a step by step guide for exact replication.
Ah I see you too are a man of culture: https://hackaday.io/project/181022-pi-pico-cat-string-flinger-toy
Nice, thanks for bringing your project to my attention.
No disrespect intended, but pet toys with batteries are a bad idea. The risk isn't worth the reward...
Become a member to follow this project and never miss any updates
Thank you; I found some stuff in the meantime but having to buy a programmer for >$100 or make one (not simple!) has dampened my enthusiasm.