STM32F030 Low Power, GPIOD_MODER geeft HardFault ?!?

Waarde CO-ers,

Ik probeer een STM32F030 zo zuinig mogelijk te laten slapen, in eerste instantie in STOP-mode. (Ik zou misschien "standbye" kunnen gebruiken als ik m'n printje aanpas, dat is nóg zuiniger. Tegenintuïtief benoemd imho, maar dat terzijde.)

Daarvoor moeten de GPIO pinnen een gedefinieerd niveau hebben omdat zwevende pinnen de ingangsbuffers anders extra stroom laten trekken. Een andere optie is de digitale ingangsbuffers uit te schakelen: Bij de STM32 kun je daarvoor de GPIO in Analog INput mode zetten, dan worden die input buffers uitgeschakeld en zijn de pinnen netjes hoogimpedant (En hoef je dus niet uit te zoeken wat een pullup, pulldown of hoog/laag niveau moet krijgen voor het laagste stroomgebruik).

Ik gebruik de STM32F030F4P6, dus de 20 pins TSSOP. Daar zitten dus niet alle pinnen op, alleen pinnen van PORTA, B en F. Maar, als ik deze als AIN zet, heb ik in sleep nog steeds een verbruik van ongeveer 0,2 mA (200uA), wat bovendien varieert (Geen stabiele meting / het verbruik daalt af en toe en stijgt weer).

Ik heb dus geprobeerd ook maar de pinnen die niet naar buiten zijn gevoerd ook op AIN te zetten, te weten GPIOC en GPIOD.

Als ik alleen GPIOC als AIN zet veranderd dit 0,2mA gebruik niet. Als ik ook GPIOD als AIN zet gebeurt er iets geks, mijn MCU lijkt vast te lopen. Met wat debuggen komt boven water dat het ding in de HardFault interrupt handler (een while(1); lusje) terecht komt.

Dat wil zeggen, als ik GPIOD_MODER probeer te schrijven: HardFault. Als ik GPIOD_MODER probeer te lezen geeft hetzelfde effect.

Kan iemand me dit verklaren?

(En eventueel gelijk vertellen of het wel nodig is deze niet naar buiten gevoerde pennen als AIN te zetten? En/Of een goede tip geven wat er nog meer voor dat "hoge" verbruik kan zorgen, ik verwacht maximaal 52uA voor de MCU VDD 48+VDDA 3.5 ; Welke peripheral(s) doen ~150uA?)

Blog // Backwards Lightspeed Bus: i*i=-1, so iic=-c.

Volgens mij is het een probleem dat je naar een register probeert te schrijven dat niet bestaat. Als jou versie geen GPIOD kent lijkt het mij sterk dat je er naar mag schrijven. Daar krijg je (denk ik) de hardfault van.

Lees dit eens door om te kunnen achterhalen wat bij jou de hardfault probleem geeft als je dat exact zou willen weten. https://www.embedded.com/design/mcus-processors-and-socs/445...based-SoCs

Het is zo moeilijk te zeggen waar je 150µA blijft. Zelf ken ik de F0 serie niet. Weet je zeker dat het in je F0 verdwijnt of hangt er nog iets aan, pull-up weerstand, i2c bus of een debugger?

Ik weet niet of je CubeMx gebruikt, maar daar staan in de repository wel wat voorbeelden van hoe deze mcu in stop mode te zetten c:\Users\xxxx\STM32Cube\Repository\STM32Cube_FW_F0_V1.9.0\Projects\STM32F0308-Discovery\Examples\PWR\PWR_CurrentConsumption[/i] daar kun je eens wat rondneuzen om te kijken wat ze nog meer uitzetten (Pwr regulator bv) voor in stop mode te gaan.

Je moet in het DATASHEET kijken. Dan zie je dat portD alleen voor 030xC devices beschikbaar is. Omdat je een '4 (16k) en niet een 'C (256k) device hebt, heeft ie geen PORTD.

Tip: Zet een breakpoint op de hardfault handler. Dan zie je in de debugger meteen dat ie daar vastloopt.

Dussss... even door het datasheet bladeren, en even snel kijken welke ports er wel en niet zijn.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

RT*bliep*M, sure.

In de reference manual (RM0360, DocID025023 Rev 3) staat GPIOA...D en F.

Oké, ik zie in de datasheet (DocID024849 Rev 2) inderdaad geen PORTC of D voor de 'F4P6 (In h4).

Er staat niet expliciet vermeld of pinnen die de TSSOP20 package niet naar buiten voert ook echt niet bestaan, (Of dus alleen maar niet naar buiten gevoerd zijn). (Ik had verwacht dat in b.v. h.3.7 te vinden, of onder tabel 2 in h2. Waar kijk jij, rew?). Dus ik dacht gewoon maar eens te proberen ze als AIN in te stellen en te kijken of dat het stroomgebruik verminderd.

GPIOC_MODER kan ik zonder enig probleem naar schrijven, terwijl die er dus óók niet is. Maar ik ben het met jullie eens dat het ontbreken van GPIOD de oorzaak is:

Ik heb op de NMI, de HardFault en de interrupts die ik zelf gebruik een handler, de rest gaat naar de default handler. Op die manier is gelijk duidelijk wélke interrupt het was. De hard fault treed op op de regel waar ik GPIOD_MODER probeer te benaderen (lezen of schrijven maakt niet uit, beiden geven een hard fault), dus dit is de oorzaak.

Waarom GPIOC dan wel kan... je ne sais pas.

Anyway.

Voor dit project gebruik ik CubeMX niet, maar ik ga alsnog even naar dat voorbeeld kijken, thanks. PWR regulator zet ik al in Low Power mode. ADC en I2C zet ik ook uit, timers idem al maakt dat niet uit (clock stopt in STOP).

De code staat op github, op m'n meest recente geklooi na: https://github.com/Lukelectro/JuggleBall/tree/feature

Ik meet de stroom vanuit de accu, maar kan ook meten voor de 3v3 regelaar (Waarbij ik dus de stroom door het led-driver subcircuit niet mee meet), of alleen de voeding voor de MCU (Waarbij ik dus de LED's, de regelaar en de ADXL345 niet mee meet). De regelaar heb ik los gemeten op 8uA. M'n leddriver trekt geen stroom als de FET's open zijn, de ADXL doet iets van 50 uA. M'n MCU ongeveer 200uA, in STOP (En zo'n 14 of 15mA in RUN).

Blog // Backwards Lightspeed Bus: i*i=-1, so iic=-c.

En het is inderdaad een stukje hardware...

Ik heb de goto_sleep() vereenvoudigd om te kunnen testen zonder de accellerometer (Anders hangt 'ie wachtend op I2C), en zo getest met een losse STM32F030 op breadboard. 30uA.

Zelfde software (met die simpeler sleep) op de "echte hardware" geeft nog steeds 200uA. (Waarvan 23uA oid voor de accelerometer, en de rest voor de MCU). (EDIT: Dit blijkt heel erg af te hangen van de voedingsspanning, maar niet lineair... De echte hardware kán ook zuinig zijn, en de testprint ook onzuinig, als ik de voedingsspanning varieer tussen de 2,5 en de 3.5V)

Goed, weet ik in elk geval waar ik moet gaan zoeken. In de hardware dus.

@rew:
Ik ben nog steeds benieuwd welk stuk datasheet ik had moeten hebben. "Kijk, zo-en-zo doe je dat het handigst" is een stuk leerzamer (en prettiger) dan "Je Doet Het Fout".

EDIT: En het stukje sleepmodecode is misschien nog nuttig voor iemand anders, dus:

c code:


void simplesleeptest() { // to test withouth adxl (on breadboard)
	// Disable interrupts
	ADC_IER &=~(BIT2|BIT3) ; //disable end of conversion interrupt (Bit2), and EOSEQ (End of Sequence) bit 3.

	// Disable pheripherals / power them down. / TODO: Make as low power as possible. See datasheet and Appnotes.

	//Power down ADC -- Is slightly more complicated then clearing aden:
	if(ADC_CR&BIT2){// if adstart (If there is a ongoing conversion)
		ADC_CR|=BIT4;	// stop it by writing adstp
		while(ADC_CR&BIT4);	// wait till adc stopped
		}
	ADC_CR|=BIT1;	// then write addis to disable adc
	while(ADC_CR&BIT0);	// wait until aden = 0 to indicate adc is disabled

  // XXX removed I2C busy check
	I2C1_CR1 &=~ BIT0; // disable I2C1 module

    //XXX removed clock switching

	// Set uC to wakeup from EXTI (On pin 11/PA5) (So only that interrupt stays enabled for now!)
	GPIOA_PUPDR|=(BIT11); //pulldown for testing without adxl
    ISER |= (BIT7); // Enable IRQ7: enable EXTI4..15 interupt in NVIC
    IPR1 |= (14<<24); // set priority for IRQ7 (4*IPRn+IRQn), starting from 0, so for IRQ7 that's IPR1 bits 31 downto 24
    //Read the relevant part of PM0215. IRC number is the position listed in RM0360 table 11.1.3.
	// Enable exti interupt on PA5:
	EXTI_IMR |= (BIT5); // and enable it in EXTI_IMR
	EXTI_RTSR |= (BIT5); // For rising edge

    GPIOA_BSRR = (BIT0); // SET PA0 (LED off)
    
    DBGMCU_CR = 0x00; //  Disable debug in STOP mode

	// In fact, let's just set all pins to analog input (No power consumption) and set them to what they're suposed to be later.
	// (TODO: remove other pin setings above that get overwritten by this anyway)
	GPIOA_MODER = 0xFFFFF3FF; // except GPIOA5, because that's INTerupt input
	GPIOB_MODER = 0xFFFFFFFF;
	GPIOF_MODER = 0xFFFFFFFF;

//btw: debug connection won't work anymore from this point onward, because it's pins are now analog inputs as well.
			

	// set MCU to sleep (STOP mode)
	SCR |= (BIT2); //set sleepdeep (Bit2) in system control register
	PWR_CR &= ~(BIT1); //Clear PDDS pwr_cr (Low power regulator)
	PWR_CR |= BIT0; //set LPDS in pwr_cr (Low power regulator)
	__asm("WFI");// Wait For Interrupt (WFI) / go to sleep

	/*SLEEPZZZzzzzzZZZZZZZZZZZzzzzzzzZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzZZZZZZZZZZZzzzzzzzzzzzzzzZZZZZZZZZZZZZZZZZZZZZz*/

	initClock(); // NB: after wakeup it runs from HSI, so initClock() again.

	// set pins same as in setup
	GPIOA_MODER = 0x28000000; // reset value.
	setup_pins();

	// Re-enable pheripherals:
	I2C1_CR1 |= BIT0; // enable I2C1 module
	setup_adc(); // re enable / re setup adc, and its interrupts

	}
Blog // Backwards Lightspeed Bus: i*i=-1, so iic=-c.

Op 11 oktober 2017 22:22:49 schreef Lucky Luke:
@rew:
Ik ben nog steeds benieuwd welk stuk datasheet ik had moeten hebben. "Kijk, zo-en-zo doe je dat het handigst" is een stuk leerzamer (en prettiger) dan "Je Doet Het Fout".

Sorry. Daar ben ik het helemaal mee eens.

"maar dat is toch logisch...." was kennelijk wat ik toen dacht.

In het DATASHEET staat een tabel met de "alternate functions". Volgens mij staat daar dan steeds een voetnoot onder met dat eea alleen op de -C versie beschikbaar is.

Volgens mij behoudt ST zich het recht voor om meer hardware te leveren dan ze beloven.

PD2 is de enige pin van portD die in de hele familie voorkomt, mogelijk wordt de die die jij hebt ook in een iets maar niet veel grotere variant gebruikt, zodat ze portC wel beschikbaar moesten maken, ook al zie je geen enkele pin aan de buitenkant.

Ik heb de indruk dat ze achter de schermen een paar OTP bitjes hebben waarmee ze delen van de chip kunnen uitzetten. Een 32k chip kan zo met bitfouten in z'n flash alsnog als 16k verkocht worden doordat ze alleen de juiste 16k flash mappen.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

In de datasheet, DocID024849 Rev 2, geeft tabel 11 de pin definities. Daar is in te zien welke pinnen naar buiten gevoerd zijn op welke package.

Tabel 12 en verder geven de alternate functions, met inderdaad voetnoten. Die voetnoten zijn dan van toepassing op de AF, en niet op de GPIO pin zelf. Alsnog geeft het geen duidelijkheid over of een pin écht niet aanwezig is, of slechts niet naar buiten gevoerd. (Want iets met OTP bitjes lijkt mij óók voor de hand liggend)

Tabel 17 echter geeft een memory map, eveneens met voetnoten of iets op een bepaald device wel of niet beschikbaar is (zoals bijvoorbeeld I2C2, met de voetnoot "This feature is available on STM32F030x8 and STM32F030xC devices only. For STM32F030x6 and STM32F060x4, the area is Reserved.").

In die tabel staat uiteraard ook de IO, en daarbij staat géén voetnoot. Dat register moet er dus gewoon zijn.

Maargoed. Dat is een zijspoor, het ging om het energiezuinig krijgen van die MCU. Dus ik heb een kale chip, zonder externe hardware anders dan de ontkoppelCtjes, voorzien van:

c code:


#include "stm32f030xx.h" // the modified Frank Duignan header file. (I started from his "Blinky" example).


void go_coma() {
	
	// no need to set up all sorts of registers, it will forget their contents anyway		

	// set MCU to sleep (STANDBYE mode)
	
	PWR_CR |= (BIT2); // clear Wake Up Flag (Takes 2 clock cycles!)
	SCR |= (BIT2); //set sleepdeep (Bit2) in system control register (see PM0215)
	PWR_CR |= (BIT1); //STANDBYE mode (Lower power then stop)
	__asm("NOP"); // make sure about those 2 cycles... 
	__asm("NOP");
	__asm("WFI");// Wait For Interrupt (WFI) / go to sleep

	/*SLEEPZZZzzzzzZZZZZZZZZZZzzzzzzzZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzZZZZZZZZZZZzzzzzzzzzzzzzzZZZZZZZZZZZZZZZZZZZZZz*/

	// on wkup, it will reset. (Except I have not set up wakeup pins, so it won't wakeup unless reset by reset pin, which is fine for test) TODO
	
	}


void goto_sleep(){
	DBGMCU_CR = 0x00; //  Disable debug in STOP mode

	// set all pins to analog input (No power consumption)
	GPIOA_MODER = 0xFFFFFFFF;
	GPIOB_MODER = 0xFFFFFFFF;
	GPIOF_MODER = 0xFFFFFFFF;

	// set MCU to sleep (STOP mode)
	SCR |= (BIT2); //set sleepdeep (Bit2) in system control register
	PWR_CR &= ~(BIT1); //stop mode
	PWR_CR |= BIT0; //set LPDS in pwr_cr (Low power regulator)
	__asm("WFI");// Wait For Interrupt (WFI) / go to sleep
	/*SLEEPZZZzzzzzZZZZZZZZZZZzzzzzzzZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzZZZZZZZZZZZzzzzzzzzzzzzzzZZZZZZZZZZZZZZZZZZZZZz*/
	GPIOA_MODER = 0x28000000; // reset value.
}


int main()
{

	goto_sleep();
	
}

main.hex

main.elf

In beide sleepmodi blijft deze chip zo'n 200 tot 600uA trekken (Wisselt per meting, afhankelijk van allerhande externe invloeden dus. Meestal rond de 400uA).
Op de eigenlijke PCB, dus met externe hardware, is dat hetzelfde.

Dan eens los de lek van de ontkoppelCtjes gemeten, die is netjes 0. (Of voldoende onmeetbaar laag dat hier geen verklaring gevonden kan worden).

Doe ik iets vauwdt fout met m'n software, of heb ik lekkende chips (Ze komen van Aliexpress, maar ik wil ook niet gelijk "Fake!" roepen...).

Chip gaat wel duidelijk slapen, stroomgebruik is lager dan in reset (1 mA) of in bootloader (7 mA)...

Blog // Backwards Lightspeed Bus: i*i=-1, so iic=-c.

Oke, enig gepruts later is het dan ook opgelost...

- Chipjes bij Reichelt gekocht ipv bij Ali. Die gedragen zich volledig hetzelfde.
- Software fout gevonden: De GPIO trekt zich niks aan van het instellen van het mode-register als het geen klok heeft... (Dat is terugkijkend heel voor de hand liggend. Het verbaast me dat het niemand anders is opgevallen.)

Dusss:

c code:


	// enable clock to Porta (BIT17), B (18), F (22)
	RCC_AHBENR |= BIT17 | BIT18 | BIT22;

en daarmee wordt de sleeptest dus:

c code:


/* Sleep test. Simple sleepmode test with no extra code.
 * Sends MCU to sleep imedeately. 
 * 
 */

#include "stm32f030xx.h" // the modified Frank Duignan header file. (I started from his "Blinky" example).


void go_coma() {
	
	// no need to set up all sorts of registers, it will forget their contents anyway		

	// set MCU to sleep (STANDBYE mode)
	
	PWR_CR |= (BIT2); // clear Wake Up Flag (Takes 2 clock cycles!)
	SCR |= (BIT2); //set sleepdeep (Bit2) in system control register (see PM0215)
	PWR_CR |= (BIT1); //STANDBYE mode (Lower power then stop)
	__asm("NOP"); // make sure about those 2 cycles... 
	__asm("NOP");
	__asm("WFI");// Wait For Interrupt (WFI) / go to sleep

	/*SLEEPZZZzzzzzZZZZZZZZZZZzzzzzzzZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzZZZZZZZZZZZzzzzzzzzzzzzzzZZZZZZZZZZZZZZZZZZZZZz*/

	// on wkup, it will reset. (Except I have not set up wakeup pins, so it won't wakeup unless reset by reset pin, which is fine for test) TODO

	
	}


void goto_sleep(){
	DBGMCU_CR = 0x00; //  Disable debug in STOP mode

	// set MCU to sleep (STOP mode)
	SCR |= (BIT2); //set sleepdeep (Bit2) in system control register
	PWR_CR &= ~(BIT1); //stop mode
	PWR_CR |= BIT0; //set LPDS in pwr_cr (Low power regulator)
	__asm("WFI");// Wait For Interrupt (WFI) / go to sleep
	/*SLEEPZZZzzzzzZZZZZZZZZZZzzzzzzzZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzZZZZZZZZZZZzzzzzzzzzzzzzzZZZZZZZZZZZZZZZZZZZZZz*/
	GPIOA_MODER = 0x28000000; // reset value.

}


int main()
{
	RCC_AHBENR |= BIT17 | BIT18 | BIT22; // enable clock to Porta (BIT17), B (18), F (22)

	// set all pins to analog input (No power consumption)
	GPIOA_MODER = 0xFFFFFFFF; 
	GPIOB_MODER = 0xFFFFFFFF;
	GPIOF_MODER = 0xFFFFFFFF;


	goto_sleep();
	
}

Dit gebruikt 17uA. (Op 'n chipje van Aliexpress, ontkoppeld volgens de HW manual met 1u+4.7u+10n+100n die dus geen van allen lekken)

Blog // Backwards Lightspeed Bus: i*i=-1, so iic=-c.

Dank voor de terugkoppeling! Ik zit ook met een "moet-in-sleep" project op de '030. Huidige status is: Klant is tevreden en heeft geklaagd dat ik te veel verander terwijl het wat hem betreft "in productie" is....

Maar het standby verbruik is erg veel. Ik denk rond de 10mA. Het overgrote deel gaat zitten in de ouderwetse lineaire regelaars met een Iq van > 5mA. Het reduceren van de CPU-stroom heeft nog geen zin totdat dat deel verbeterd is....

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/