The follow steps show you how to DIY SwitchBot using Silicon Labs Thunderboard BG22 Kit and SG90 servo motor.
1. Add the following header files, defines, and global variables in app.c
#include "em_device.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_chip.h"
#include "em_gpio.h"
#include "em_timer.h"
// Global variables used to set top value and duty cycle of the timer
#define PWM_FREQ 50
#define DUTY_CYCLE_STEPS 0.05
static uint32_t topValue = 0;
static volatile float dutyCycle = 0;
2. Add the following functions for PWM (original from here) and SG90 servo motor control.
/**************************************************************************//**
* @brief
* Interrupt handler for TIMER0 that changes the duty cycle
*
* @note
* This handler doesn't actually dynamically change the duty cycle. Instead,
* it acts as a template for doing so. Simply change the dutyCycle
* global variable here to dynamically change the duty cycle.
*****************************************************************************/
void TIMER0_IRQHandler(void)
{
// Acknowledge the interrupt
uint32_t flags = TIMER_IntGet(TIMER0);
TIMER_IntClear(TIMER0, flags);
// Update CCVB to alter duty cycle starting next period
TIMER_CompareBufSet(TIMER0, 0, (uint32_t)(topValue * dutyCycle));
}
/**************************************************************************//**
* @brief
* GPIO initialization
*****************************************************************************/
void initGpio(void)
{
// Configure PA6 as output
GPIO_PinModeSet(gpioPortA, 6, gpioModePushPull, 0);
}
/**************************************************************************//**
* @brief
* CMU initialization
*****************************************************************************/
void initCmu(void)
{
// Enable clock to GPIO and TIMER0
CMU_ClockEnable(cmuClock_GPIO, true);
CMU_ClockEnable(cmuClock_TIMER0, true);
}
/**************************************************************************//**
* @brief
* TIMER initialization
*****************************************************************************/
void initTimer(void)
{
uint32_t timerFreq = 0;
// Initialize the timer
TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
// Configure TIMER0 Compare/Capture for output compare
TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
// Use PWM mode, which sets output on overflow and clears on compare events
timerInit.prescale = timerPrescale64;
timerInit.enable = false;
timerCCInit.mode = timerCCModePWM;
// Configure but do not start the timer
TIMER_Init(TIMER0, &timerInit);
// Route Timer0 CC0 output to PA6
GPIO->TIMERROUTE[0].ROUTEEN = GPIO_TIMER_ROUTEEN_CC0PEN;
GPIO->TIMERROUTE[0].CC0ROUTE = (gpioPortA << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT)
| (6 << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT);
// Configure CC Channel 0
TIMER_InitCC(TIMER0, 0, &timerCCInit);
// Start with 10% duty cycle
dutyCycle = DUTY_CYCLE_STEPS;
// set PWM period
timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1);
topValue = (timerFreq / PWM_FREQ);
// Set top value to overflow at the desired PWM_FREQ frequency
TIMER_TopSet(TIMER0, topValue);
// Set compare value for initial duty cycle
TIMER_CompareSet(TIMER0, 0, (uint32_t)(topValue * dutyCycle));
// Start the timer
TIMER_Enable(TIMER0, true);
// Enable TIMER0 compare event interrupts to update the duty cycle
TIMER_IntEnable(TIMER0, TIMER_IEN_CC0);
NVIC_EnableIRQ(TIMER0_IRQn);
}
void pwm_turn_on_sg90(void);
void pwm_turn_off_sg90(void);
void pwm_turn_on_sg90(void)
{
gecko_cmd_hardware_set_soft_timer (TIMER_MS_2_TIMERTICK(100),SERVO_0_TIMER, true);
}
void pwm_turn_off_sg90(void)
{
gecko_cmd_hardware_set_soft_timer (TIMER_MS_2_TIMERTICK(100),SERVO_180_TIMER, true);
}
3. Add the following three timer events in red in app_timer.h
typedef enum {
UI_TIMER = 0,
ADV_ALTERNATE_TIMER,
IMU_SERVICE_ACC_TIMER,
IMU_SERVICE_ORI_TIMER,
BATT_SERVICE_TIMER,
SENSOR_READOUT_TIMER,
/** Temperature measurement timer.
* This is an auto-reload timer used for timing temperature measurements. */
TEMP_TIMER,
SERVO_0_TIMER,
SERVO_90_TIMER,
SERVO_180_TIMER
} appTimer_t;
4. Add the following code in appInit() of app.c to init PWM.
initCmu();
initGpio();
initTimer();
5. Add the following three servo motor control event in "case gecko_evt_hardware_soft_timer_id:..." of appHandleEvents() in app.c.
case SERVO_0_TIMER:
dutyCycle = 0.1;
// Set compare value for initial duty cycle
TIMER_CompareSet(TIMER0, 0, (uint32_t)(topValue * dutyCycle));
gecko_cmd_hardware_set_soft_timer (TIMER_MS_2_TIMERTICK(300),SERVO_90_TIMER, true);
break;
case SERVO_90_TIMER:
dutyCycle = 0.05;
// Set compare value for initial duty cycle
TIMER_CompareSet(TIMER0, 0, (uint32_t)(topValue * dutyCycle));
//gecko_cmd_hardware_set_soft_timer (TIMER_MS_2_TIMERTICK(300),SERVO_0_TIMER, true);
break;
case SERVO_180_TIMER:
dutyCycle = 0.02;
// Set compare value for initial duty cycle
TIMER_CompareSet(TIMER0, 0, (uint32_t)(topValue * dutyCycle));
gecko_cmd_hardware_set_soft_timer (TIMER_MS_2_TIMERTICK(300),SERVO_90_TIMER, true);
break;
6. Call pwm_turn_on_sg90/pwm_turn_off_sg90 in aioDeviceDigitalOutWrite() of aio.c to control SG90 according to LED characteristics.
7. Build and download firmware into Thunderboard BG22 Kit.
8. Connect GND(EXP1)/3V0(EXP20)/PA6(EXP14) pins on Thunderboard BG22 Kit to BROWN(GND)/RED(PWR)/ORANGE(PWM) on SG90.
9. Install Silicon Labs Thunderboard App on your SmartPhone and connect to Thunderboard BG22 Kit to control light switch.
Very nice demo. Thank you!
ReplyDelete