MBed, STM32Nucleo and complimentary outputs.

After a few days of trying to get the complimentary outputs on the STM32F103 Nucleo going I’ve finally succeeded. It was surprisingly simple in the end but there is a general lack of documentation and I wonder at the completeness of the PWM API. Anyway, here is a platform specific working example. It produces a two channels of complimentary output signals (i.e. 4 outputs in total) running at 1kHz with 80% duty and an deadtime of about 13.5uS.

#include "mbed.h"
DigitalOut  my_led(LED1);

/*
Pin mappings for Timer 1 (the advanced timer with deadtime)
From: https://developer.mbed.org/users/mbed_official/code/mbed-src/file/a11c0372f0ba/targets/hal/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/PeripheralPins.c
    {PA_8,  PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH1 - Default
    {PA_9,  PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH2 - Default
    {PA_10, PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH3 - Default
    {PB_13, PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH1N - Default
    {PB_14, PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH2N - Default
    {PB_15, PWM_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, 0)}, // TIM1_CH3N - Default
 
*/

int main()
{

    PwmOut      PhaATop(PA_8);    
    PwmOut      PhaABtm(PB_13);  // This should be the complement of PA_8    
    PwmOut      PhaBTop(PA_9);    
    PwmOut      PhaBBtm(PB_14);  // This should be the complement of PA_9   
    PhaATop.period_ms(1);
    PhaATop = 0.8;       
    PhaBTop.period_ms(1);
    PhaBTop = 0.8;             
    TIM1->CCER |= 4; //enable ch1 complimentary output
    TIM1->BDTR |= 0xff; // specify the maximum amount of deadtime required approx 13.5us - see page 356 of reference manual
    TIM1->CCER |= 64; //enable ch2 complimentary output
    TIM1->BDTR |= 0xff; // specify the maximum amount of deadtime required approx 13.5us - see page 356 of reference manual
    while (1) {
        my_led = !my_led;
        wait(0.2); // 500 ms
    }
}

Minimal blinky in ARM Cortex M0 assembler

Update: A variation of this for the LPC11148FN28 can be found here
I wanted to try to write a minimal assembler blinky program that flashes the blue LED on an STM32F0 Value Line discovery board (STM32F030R8T6 MCU).
The linker script and Makefile are tagged on at the end

// Minimal assembler blinky example for stm32f030 
// (stm32f0 Value line discovery board)
// LED is on GPIOC bit 8

		.thumb  	// Cortex micros only understand thumb(2) code	
		.syntax unified // use newer style instructions
			
		.text		// what follows is code
// Set up interrupt vector table: 
// entry 0: initial stack pointer
// entry 1: reset address
Vector_Table:    
		.word     0x20002000          // Top of Stack 
/* The interrupt vectors that follow are manually forced 
   to be aligned along odd addresses.  The reason for this
   is that the address for a Thumb instruction must always 
   have its LSB set to 1.  This does not mean that the 
   instruction is stored at an odd number address.  The 
   LSB is used as a flag to indicate that an ARM (LSB=0) 
   or a Thumb (LSB=1) instruction is being addressed. 
*/
ResetVector:    .word     start + 1     // Reset Handler 

// the program starts here 
start:
// Do initial IO configuration
// Turn on clock for GPIOC		
		ldr R1,=RCCAHBENR		// pointer to register 
		ldr R2,=RCCAHBENRMASK	// mask
		bl	set_bits
// Make port pin behave as an output
		ldr R1,=GPIOCMODER		// pointer to register 
		ldr R2,=GPIOCMODERMASK	// mask
		bl	set_bits
		
// Now enter the main loop
main_loop:
// Turn LED on
		ldr R1,=GPIOCODR	// pointer to register 
		ldr R2,=LEDMASK		// mask
		bl 	set_bits
// wait a while
		ldr R0,=DELAYLENGTH		
		bl delay

// Turn LED off
		ldr R1,=GPIOCODR	// pointer to register 
		ldr R2,=LEDMASK		// mask
		bl	clear_bits

// wait a while
		ldr R0,=DELAYLENGTH
		bl delay

		b	main_loop		// branch back to start of loop 
		
// subroutines are down here		
// Delay subroutine.  Pass the length of the delay in R0
delay:	subs R0,R0,#1
		bne delay
		bx LR

// on entry, R1 contains address where target address is stored
// R2 contains address where mask bits are stored
set_bits: 
		ldr R0,[R1]			// read register contents
		orrs R0,R0,R2		// combine with register contents
		str R0,[R1]			// write back contents
		bx LR				// return to caller
// on entry, R1 contains address where target address is stored
// R2 contains address where mask bits are stored
clear_bits: 
		ldr R0,[R1]			// read register contents
		bics R0,R0,R2		// combine with register contents
		str R0,[R1]			// write back contents
		bx LR				// return to caller
		
	
// constant definitions are below here.

		.equ RCCAHBENR,	0x40021014
		.equ GPIOCMODER, 0x48000800
		.equ GPIOCODR,	0x48000814


		.equ RCCAHBENRMASK,	0x00080000
		.equ GPIOCMODERMASK, 0x00010000
		.equ LEDMASK, 0x00000100
		.equ DELAYLENGTH, 0x000fffff


Linker script

MEMORY
{
    flash : org = 0x08000000, len = 64k
    ram : org = 0x20000000, len = 8k
}
  
SECTIONS
{
        
	. = ORIGIN(flash);
        .text : {
		
        } >flash
	. = ORIGIN(ram);
        .data : {
              		
        } >ram
}

Makefile

# Specify the assembler to use
AS=arm-none-eabi-as
# Specity the linker to use
LD=arm-none-eabi-ld

ASFLAGS=-mcpu=cortex-m0 -mthumb -g 
# List the object files involved in this project
OBJS=	blinky.o 
blinky.elf : $(OBJS)
	$(LD) $(OBJS) -T linker_script.ld --cref -Map blinky.map -nostartfiles -o main.elf
	 objcopy -O ihex main.elf main.hex
blinky.o: blinky.s
	$(AS) $(ASFLAGS) blinky.s -asghl=blinky.lst -o blinky.o
# if someone types in 'make clean' then remove all object files and executables
# associated wit this project
clean: 
	rm $(OBJS) 
	rm blinky.elf 

Low power sensor node follow up to follow up

The STM32F030/NRF24L01+ low power sensor node has been running since August 14 on a pair of cheap generic AA batteries. The node transmits data (brightness) every second and will retry up to 15 times if it fails to get an ACK from the base station. For most of this time the base station has been off so those retries have been happening. Energy consumption has been much higher than it should have been nevertheless the device is still running.