diff --git a/stmhal/Makefile b/stmhal/Makefile
index 0f3fe46cfabf8ead4700d26e1243a97c5a5a766b..fff966934abab2a8f468aa0a2c2ad718fc123b9f 100644
--- a/stmhal/Makefile
+++ b/stmhal/Makefile
@@ -61,6 +61,7 @@ SRC_C = \
 	usbd_msc_storage.c \
 	pendsv.c \
 	systick.c  \
+	timer.c \
 	led.c \
 	pin.c \
 	pin_map.c \
@@ -98,7 +99,6 @@ SRC_C = \
 	adc.c \
 	i2c.c \
 
-#	timer.c \
 #	pybwlan.c \
 
 SRC_S = \
diff --git a/stmhal/led.c b/stmhal/led.c
index b7afdd40c5cf625771e2de54b8edfc50d50a98da..3108ee7792a47204e3d73e1edcf3749d67aa144b 100644
--- a/stmhal/led.c
+++ b/stmhal/led.c
@@ -1,7 +1,5 @@
 #include <stdio.h>
 #include <stm32f4xx_hal.h>
-#include "usbd_cdc_msc_hid.h"
-#include "usbd_cdc_interface.h"
 
 #include "nlr.h"
 #include "misc.h"
@@ -9,6 +7,7 @@
 #include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
+#include "timer.h"
 #include "led.h"
 #include "pin.h"
 #include "build/pins.h"
@@ -54,6 +53,8 @@ void led_init(void) {
     // LED4 (blue) is on PB4 which is TIM3_CH1
     // we use PWM on this channel to fade the LED
 
+    // LED3 (yellow) is on PA15 which has TIM2_CH1, so we could PWM that as well
+
     // GPIO configuration
     GPIO_InitStructure.Pin = MICROPY_HW_LED4.pin_mask;
     GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
diff --git a/stmhal/main.c b/stmhal/main.c
index d6d9b9ed7eebe26a3b729d67448b4f589862a676..afef84e290f9ff2f8609894c3abf3a3d158020d2 100644
--- a/stmhal/main.c
+++ b/stmhal/main.c
@@ -22,6 +22,7 @@
 #include "readline.h"
 #include "pyexec.h"
 #include "usart.h"
+#include "timer.h"
 #include "led.h"
 #include "exti.h"
 #include "usrsw.h"
@@ -38,7 +39,6 @@
 #include "dac.h"
 #include "pin.h"
 #if 0
-#include "timer.h"
 #include "pybwlan.h"
 #endif
 
@@ -177,6 +177,7 @@ int main(void) {
 
     // basic sub-system init
     pendsv_init();
+    timer_tim3_init();
     led_init();
     switch_init0();
 
@@ -409,6 +410,11 @@ soft_reset:
     rng_init();
 #endif
 
+#if MICROPY_HW_ENABLE_TIMER
+    // timer
+    //timer_init();
+#endif
+
     // I2C
     i2c_init();
 
@@ -422,13 +428,6 @@ soft_reset:
     servo_init();
 #endif
 
-#if 0
-#if MICROPY_HW_ENABLE_TIMER
-    // timer
-    timer_init();
-#endif
-#endif
-
 #if MICROPY_HW_ENABLE_DAC
     // DAC
     dac_init();
diff --git a/stmhal/servo.c b/stmhal/servo.c
index 15ba35165cb40fad2db29c2ee533a06062bfc35c..9c757c56596a6688645d4d14db12836ab4ec7dc1 100644
--- a/stmhal/servo.c
+++ b/stmhal/servo.c
@@ -8,12 +8,13 @@
 #include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
+#include "timer.h"
 #include "servo.h"
 
 // this servo driver uses hardware PWM to drive servos on PA0, PA1, PA2, PA3 = X1, X2, X3, X4
 // TIM2 and TIM5 have CH1, CH2, CH3, CH4 on PA0-PA3 respectively
 // they are both 32-bit counters with 16-bit prescaler
-// we use TIM2
+// we use TIM5
 
 #define PYB_SERVO_NUM (4)
 
@@ -30,23 +31,8 @@ STATIC const mp_obj_type_t servo_obj_type;
 
 STATIC pyb_servo_obj_t pyb_servo_obj[PYB_SERVO_NUM];
 
-TIM_HandleTypeDef TIM2_Handle;
-
 void servo_init(void) {
-    // TIM2 clock enable
-    __TIM2_CLK_ENABLE();
-
-    // set up and enable interrupt
-    HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0);
-    HAL_NVIC_EnableIRQ(TIM2_IRQn);
-
-    // PWM clock configuration
-    TIM2_Handle.Instance = TIM2;
-    TIM2_Handle.Init.Period = 2000; // timer cycles at 50Hz
-    TIM2_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz
-    TIM2_Handle.Init.ClockDivision = 0;
-    TIM2_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
-    HAL_TIM_PWM_Init(&TIM2_Handle);
+    timer_tim5_init();
 
     // reset servo objects
     for (int i = 0; i < PYB_SERVO_NUM; i++) {
@@ -74,17 +60,17 @@ void servo_timer_irq_callback(void) {
                 need_it = true;
             }
             switch (s->servo_id) {
-                case 1: TIM2->CCR1 = s->pulse_cur; break;
-                case 2: TIM2->CCR2 = s->pulse_cur; break;
-                case 3: TIM2->CCR3 = s->pulse_cur; break;
-                case 4: TIM2->CCR4 = s->pulse_cur; break;
+                case 1: TIM5->CCR1 = s->pulse_cur; break;
+                case 2: TIM5->CCR2 = s->pulse_cur; break;
+                case 3: TIM5->CCR3 = s->pulse_cur; break;
+                case 4: TIM5->CCR4 = s->pulse_cur; break;
             }
         }
     }
     if (need_it) {
-        __HAL_TIM_ENABLE_IT(&TIM2_Handle, TIM_IT_UPDATE);
+        __HAL_TIM_ENABLE_IT(&TIM5_Handle, TIM_IT_UPDATE);
     } else {
-        __HAL_TIM_DISABLE_IT(&TIM2_Handle, TIM_IT_UPDATE);
+        __HAL_TIM_DISABLE_IT(&TIM5_Handle, TIM_IT_UPDATE);
     }
 }
 
@@ -105,7 +91,7 @@ STATIC void servo_init_channel(pyb_servo_obj_t *s) {
     GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
     GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
     GPIO_InitStructure.Pull = GPIO_NOPULL;
-    GPIO_InitStructure.Alternate = GPIO_AF1_TIM2;
+    GPIO_InitStructure.Alternate = GPIO_AF2_TIM5;
     HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
 
     // PWM mode configuration
@@ -114,10 +100,10 @@ STATIC void servo_init_channel(pyb_servo_obj_t *s) {
     oc_init.Pulse = s->pulse_cur; // units of 10us
     oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
     oc_init.OCFastMode = TIM_OCFAST_DISABLE;
-    HAL_TIM_PWM_ConfigChannel(&TIM2_Handle, &oc_init, channel);
+    HAL_TIM_PWM_ConfigChannel(&TIM5_Handle, &oc_init, channel);
 
     // start PWM
-    HAL_TIM_PWM_Start(&TIM2_Handle, channel);
+    HAL_TIM_PWM_Start(&TIM5_Handle, channel);
 }
 
 /******************************************************************************/
@@ -129,10 +115,10 @@ STATIC mp_obj_t pyb_servo_set(mp_obj_t port, mp_obj_t value) {
     if (v < 50) { v = 50; }
     if (v > 250) { v = 250; }
     switch (p) {
-        case 1: TIM2->CCR1 = v; break;
-        case 2: TIM2->CCR2 = v; break;
-        case 3: TIM2->CCR3 = v; break;
-        case 4: TIM2->CCR4 = v; break;
+        case 1: TIM5->CCR1 = v; break;
+        case 2: TIM5->CCR2 = v; break;
+        case 3: TIM5->CCR3 = v; break;
+        case 4: TIM5->CCR4 = v; break;
     }
     return mp_const_none;
 }
@@ -142,8 +128,8 @@ MP_DEFINE_CONST_FUN_OBJ_2(pyb_servo_set_obj, pyb_servo_set);
 STATIC mp_obj_t pyb_pwm_set(mp_obj_t period, mp_obj_t pulse) {
     int pe = mp_obj_get_int(period);
     int pu = mp_obj_get_int(pulse);
-    TIM2->ARR = pe;
-    TIM2->CCR3 = pu;
+    TIM5->ARR = pe;
+    TIM5->CCR3 = pu;
     return mp_const_none;
 }
 
diff --git a/stmhal/servo.h b/stmhal/servo.h
index d5fb6a8505df7ff0cd813cb48d907b1ea6ea25aa..865b0fc9f550eedf3d82836f962be674bd7d4ce0 100644
--- a/stmhal/servo.h
+++ b/stmhal/servo.h
@@ -1,5 +1,3 @@
-extern TIM_HandleTypeDef TIM2_Handle;
-
 void servo_init(void);
 void servo_timer_irq_callback(void);
 
diff --git a/stmhal/stm32f4xx_hal_msp.c b/stmhal/stm32f4xx_hal_msp.c
index 1cf78336f85cfdaf8067fc6f673226512ea30ef9..7004912e145593ddfc53c8b48dd6ce3c2bf26225 100644
--- a/stmhal/stm32f4xx_hal_msp.c
+++ b/stmhal/stm32f4xx_hal_msp.c
@@ -38,8 +38,6 @@
 
 /* Includes ------------------------------------------------------------------*/
 #include "stm32f4xx_hal.h"
-#include "usbd_cdc_msc_hid.h"
-#include "usbd_cdc_interface.h"
 
 #include "misc.h"
 #include "mpconfig.h"
@@ -47,30 +45,12 @@
 #include "obj.h"
 #include "servo.h"
 
-TIM_HandleTypeDef TIM3_Handle;
-
 /**
   * @brief  Initializes the Global MSP.
   * @param  None
   * @retval None
   */
 void HAL_MspInit(void) {
-    // set up the timer for USBD CDC
-    __TIM3_CLK_ENABLE();
-
-    TIM3_Handle.Instance = TIM3;
-    TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
-    TIM3_Handle.Init.Prescaler = 84-1;
-    TIM3_Handle.Init.ClockDivision = 0;
-    TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
-    HAL_TIM_Base_Init(&TIM3_Handle);
-
-    HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
-    HAL_NVIC_EnableIRQ(TIM3_IRQn);
-
-    if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) {
-        /* Starting Error */
-    }
 }
 
 /**
@@ -79,9 +59,6 @@ void HAL_MspInit(void) {
   * @retval None
   */
 void HAL_MspDeInit(void) {
-    // reset TIM3 timer
-    __TIM3_FORCE_RESET();
-    __TIM3_RELEASE_RESET();
 }
 
 /**
@@ -146,14 +123,6 @@ void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc)
    __HAL_RCC_RTC_DISABLE();
 }
 
-void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
-    if (htim == &TIM3_Handle) {
-        USBD_CDC_HAL_TIM_PeriodElapsedCallback();
-    } else if (htim == &TIM2_Handle) {
-        servo_timer_irq_callback();
-    }
-}
-
 /**
   * @}
   */
diff --git a/stmhal/stm32f4xx_it.c b/stmhal/stm32f4xx_it.c
index e74b1c6c75a0a0e18146d6420f41781db1941b72..aee689d7e2001e6c6cbd2f9952906a50fc8cb9aa 100644
--- a/stmhal/stm32f4xx_it.c
+++ b/stmhal/stm32f4xx_it.c
@@ -42,15 +42,13 @@
 
 #include "stm32f4xx_it.h"
 #include "stm32f4xx_hal.h"
-#include "usbd_cdc_msc_hid.h"
-#include "usbd_cdc_interface.h"
 
 #include "misc.h"
 #include "mpconfig.h"
 #include "qstr.h"
 #include "obj.h"
 #include "exti.h"
-#include "servo.h"
+#include "timer.h"
 
 /** @addtogroup STM32F4xx_HAL_Examples
   * @{
@@ -351,12 +349,12 @@ void RTC_WKUP_IRQHandler(void) {
     Handle_EXTI_Irq(EXTI_RTC_WAKEUP);
 }
 
-void TIM2_IRQHandler(void) {
-    HAL_TIM_IRQHandler(&TIM2_Handle);
-}
-
 void TIM3_IRQHandler(void) {
     HAL_TIM_IRQHandler(&TIM3_Handle);
 }
 
+void TIM5_IRQHandler(void) {
+    HAL_TIM_IRQHandler(&TIM5_Handle);
+}
+
 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/stmhal/timer.c b/stmhal/timer.c
new file mode 100644
index 0000000000000000000000000000000000000000..cfdb93587fe345c221b63e75f8fd23f0e92fdd08
--- /dev/null
+++ b/stmhal/timer.c
@@ -0,0 +1,189 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <stm32f4xx_hal.h>
+#include "usbd_cdc_msc_hid.h"
+#include "usbd_cdc_interface.h"
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "runtime.h"
+#include "timer.h"
+#include "servo.h"
+
+// The timers can be used by multiple drivers, and need a common point for
+// the interrupts to be dispatched, so they are all collected here.
+//
+// TIM3:
+//  - USB CDC interface, interval, to check for new data
+//  - LED 4, PWM to set the LED intensity
+//
+// TIM5:
+//  - servo controller, PWM
+
+TIM_HandleTypeDef TIM3_Handle;
+TIM_HandleTypeDef TIM5_Handle;
+
+// TIM3 is set-up for the USB CDC interface
+void timer_tim3_init(void) {
+    // set up the timer for USBD CDC
+    __TIM3_CLK_ENABLE();
+
+    TIM3_Handle.Instance = TIM3;
+    TIM3_Handle.Init.Period = (USBD_CDC_POLLING_INTERVAL*1000) - 1;
+    TIM3_Handle.Init.Prescaler = 84-1;
+    TIM3_Handle.Init.ClockDivision = 0;
+    TIM3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
+    HAL_TIM_Base_Init(&TIM3_Handle);
+
+    HAL_NVIC_SetPriority(TIM3_IRQn, 6, 0);
+    HAL_NVIC_EnableIRQ(TIM3_IRQn);
+
+    if (HAL_TIM_Base_Start(&TIM3_Handle) != HAL_OK) {
+        /* Starting Error */
+    }
+}
+
+/* unused
+void timer_tim3_deinit(void) {
+    // reset TIM3 timer
+    __TIM3_FORCE_RESET();
+    __TIM3_RELEASE_RESET();
+}
+*/
+
+// TIM5 is set-up for the servo controller
+void timer_tim5_init(void) {
+    // TIM5 clock enable
+    __TIM5_CLK_ENABLE();
+
+    // set up and enable interrupt
+    HAL_NVIC_SetPriority(TIM5_IRQn, 6, 0);
+    HAL_NVIC_EnableIRQ(TIM5_IRQn);
+
+    // PWM clock configuration
+    TIM5_Handle.Instance = TIM5;
+    TIM5_Handle.Init.Period = 2000; // timer cycles at 50Hz
+    TIM5_Handle.Init.Prescaler = ((SystemCoreClock / 2) / 100000) - 1; // timer runs at 100kHz
+    TIM5_Handle.Init.ClockDivision = 0;
+    TIM5_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
+    HAL_TIM_PWM_Init(&TIM5_Handle);
+}
+
+// Interrupt dispatch
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
+    if (htim == &TIM3_Handle) {
+        USBD_CDC_HAL_TIM_PeriodElapsedCallback();
+    } else if (htim == &TIM5_Handle) {
+        servo_timer_irq_callback();
+    }
+}
+
+// below is old code from stm/ which has not yet been fully ported to stmhal/
+#if 0
+typedef struct _pyb_hal_tim_t {
+    mp_obj_base_t base;
+    TIM_HandleTypeDef htim;
+} pyb_hal_tim_t;
+
+pyb_hal_tim_t pyb_hal_tim_6;
+
+    pyb_hal_tim_6 = {
+        .base = {&pyb_type_hal_tim};
+        .htim = {TIM6
+
+// TIM6 is used as an internal interrup to schedule something at a specific rate
+mp_obj_t timer_py_callback;
+
+mp_obj_t timer_py_set_callback(mp_obj_t f) {
+    timer_py_callback = f;
+    return mp_const_none;
+}
+
+mp_obj_t timer_py_set_period(mp_obj_t period) {
+    TIM6->ARR = mp_obj_get_int(period) & 0xffff;
+    return mp_const_none;
+}
+
+mp_obj_t timer_py_set_prescaler(mp_obj_t prescaler) {
+    TIM6->PSC = mp_obj_get_int(prescaler) & 0xffff;
+    return mp_const_none;
+}
+
+mp_obj_t timer_py_get_value(void) {
+    return mp_obj_new_int(TIM6->CNT & 0xfffff);
+}
+
+void timer_init(void) {
+    timer_py_callback = mp_const_none;
+
+    // TIM6 clock enable
+    __TIM6_CLK_ENABLE();
+
+    // Compute the prescaler value so TIM6 runs at 20kHz
+    uint16_t PrescalerValue = (uint16_t) ((SystemCoreClock / 2) / 20000) - 1;
+
+    // Time base configuration
+    tim_handle.Instance = TIM6;
+    tim_handle.Init.Prescaler = PrescalerValue;
+    tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6
+    tim_handle.Init.Period = 20000; // timer cycles at 1Hz
+    tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // unused for TIM6
+    tim_handle.Init.RepetitionCounter = 0; // unused for TIM6
+    HAL_TIM_Base_Init(&tim_handle);
+
+    // enable perhipheral preload register
+    //TIM_ARRPreloadConfig(TIM6, ENABLE); ??
+
+    // set up interrupt
+    HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0xf, 0xf); // lowest priority
+    HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
+
+    // start timer, so that it interrupts on overflow
+    HAL_TIM_Base_Start_IT(&tim_handle);
+
+    // Python interface
+    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("timer"));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("callback"), rt_make_function_n(1, timer_py_set_callback));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("period"), rt_make_function_n(1, timer_py_set_period));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("prescaler"), rt_make_function_n(1, timer_py_set_prescaler));
+    rt_store_attr(m, QSTR_FROM_STR_STATIC("value"), rt_make_function_n(0, timer_py_get_value));
+    rt_store_name(QSTR_FROM_STR_STATIC("timer"), m);
+}
+
+void timer_interrupt(void) {
+    if (timer_py_callback != mp_const_none) {
+        nlr_buf_t nlr;
+        if (nlr_push(&nlr) == 0) {
+            // XXX what to do if the GC is in the middle of running??
+            rt_call_function_0(timer_py_callback);
+            nlr_pop();
+        } else {
+            // uncaught exception
+            printf("exception in timer interrupt\n");
+            mp_obj_print((mp_obj_t)nlr.ret_val, PRINT_REPR);
+            printf("\n");
+        }
+    }
+}
+
+mp_obj_t pyb_Timer(mp_obj_t timx_in) {
+    TIM_TypeDef *TIMx = (TIM_TypeDef*)mp_obj_get_int(timx_in);
+    if (!IS_TIM_INSTANCE(TIMx)) {
+        nlr_jump(mp_obj_new_exception_msg(&mp_type_ValueError, "argument 1 is not a TIM instance"));
+    }
+    pyb_hal_tim_t *tim = m_new_obj(pyb_hal_tim_t);
+    tim->htim.Instance = TIMx;
+    tim->htim.Instance.Init.Prescaler = x;
+    tim->htim.Instance.Init.CounterMode = y;
+    tim->htim.Instance.Init.Period = y;
+    tim->htim.Instance.Init.ClockDivision = y;
+    tim->htim.Instance.Init.RepetitionCounter = y;
+    HAL_TIM_Base_Init(&tim->htim);
+    return tim;
+}
+#endif
diff --git a/stmhal/timer.h b/stmhal/timer.h
new file mode 100644
index 0000000000000000000000000000000000000000..317b39b3f714e8518b82d2c1b88fbe662e60f48b
--- /dev/null
+++ b/stmhal/timer.h
@@ -0,0 +1,10 @@
+// Periodically, the state of the buffer "UserTxBuffer" is checked.
+// The period depends on USBD_CDC_POLLING_INTERVAL
+// The value is in ms. The max is 65 and the min is 1.
+#define USBD_CDC_POLLING_INTERVAL (10)
+
+extern TIM_HandleTypeDef TIM3_Handle;
+extern TIM_HandleTypeDef TIM5_Handle;
+
+void timer_tim3_init(void);
+void timer_tim5_init(void);
diff --git a/stmhal/usbd_cdc_interface.h b/stmhal/usbd_cdc_interface.h
index 5ed5ecef46bc73d42032faf392567d1eb7ace539..88ea7e5afc0a4a15a9c4e22244d03566d74b951a 100644
--- a/stmhal/usbd_cdc_interface.h
+++ b/stmhal/usbd_cdc_interface.h
@@ -33,10 +33,6 @@
 /* Exported types ------------------------------------------------------------*/
 /* Exported constants --------------------------------------------------------*/
 
-/* Periodically, the state of the buffer "UserTxBuffer" is checked.
-   The period depends on USBD_CDC_POLLING_INTERVAL */
-#define USBD_CDC_POLLING_INTERVAL             10 /* in ms. The max is 65 and the min is 1 */
-
 extern TIM_HandleTypeDef TIM3_Handle;
 extern const USBD_CDC_ItfTypeDef USBD_CDC_fops;