Actions

Programmering STM32

From Chalmers Robotics

Revision as of 21:22, 19 January 2015 by Sterna (talk | contribs) (De_init-funktionen)

Denna sida är under konstruktion, men kommer att innehålla information om vad man bör tänka på när man programmerar STM32 (och delvis generellt ARM). Notera att denna sida endast handlar om att skriva kod, inte om hur man får programmet att bygga osv. Detta tas upp i artiklarna Förberedelse programmering STM32 och Byggprocess ARM

Tillvägagångssätt för programmering

Innan du kan sätta och och börja koda för STM32 behöver du sätta upp mjukvara för att kunna kompilera koden. Ifall du inte har gjort det, läs Förberedelser för programmering av STM32.

Dokumentation för STM32

Här är en länk till STs standardlib (det är svårare att hitta än man kan tro...). dessa dokument vill du ladda hem, och läsa i för att förstå hur just den funktion du vill ha fungerar. reference sheet datasheet disovery board datasheet

Tips om Eclipse

Filstruktur i projekt

i src lägger du koden du skriver, som slutar på .c

i lib finns bibliotek, främst stdlib från ST, men du kan även ta hem andra bibliotek i framtiden

i inc lägger du alla .h filer

makefile är den fil som läses av programmet make varje gång du trycker på kompilera i Eclipse. Det är ungefär som en göralista som styr vad som ska utföras av make, för att alla filer ska kompileras, allt ska länkas etc. Den skriver du antingen sjläv (baserat på en existerande såklart) eller så låter du Eclipse generera den åt dig.

Här behöver du lägga till nya filer du skriver under SRC, annars kommer inte make ta med dem i kompileringen

kompilera i eclipse

skicka över med st-link program

Testprogram för introARM

Debugging

Blinka med LEDs och UART.

Skriva C-kod

Grunderna

Är du ny? Läs lite i C-bok eller läs exempelkod! Här ska massa länkar till bra material Att lära sig C programmering finnas

skriva kod för stm32

processorer med en ARM kärna skiljer sig från microprocessorer från Atmel och PIC i grund och botten i att .... kärna från arm, periferienheter från ST

Structer

Man skriver inte i register, utan man använder ett abstraheringslager, stdlib, med funktioner och structer etc så bör man inte ändra direkt i register

Klockor

finns en uppsjö av klockor i STM32, se "karta"(?)

Exempelkod

för introARM / discovery board gpio uart timer interrupt

Programmering

Som alla vet programmerar man i C.

Felsökning

Om du setat länge med ett problem finns det några standardmisstag om man brukar göra. Kolla igenom följande checklista så kanske du hittar ditt problem. Här ges även lite allmänna tips på felsökning:

Bättra på lödningar

Ingen kod är bättre än den hårdvara den körs på. Bättre på dina lödningar och mät så att signalen kommer fram.

Bygg rent!

Många problem kan komma av att det finns gammal kod kvar som kompilatorn inte tycker den ska bygga igen (typiskt om du bara uppdaterat en h-fil). Om du kör clean på ditt projekt och sedan bygger igen kanske detta löser en del problem.

Har du givit klocka till peripheral?

Alla peripherals måste ha klocka. Det första man bör gör när man initar någonting peripheral är att anropa funktionen RCC_APBxPeriphClockCmd(RCC_APBxPeriph_PPPPP,ENABLE) där x är den buss din peripheral sitter på och RCC_APBxPeriph_PPPPP är makrot för den peripheral du ska slå igång. Det finns även för RCC_AHB-bussen också. GPIO sitter på APB2 exempelvis. Notera att alla moduler som är inblandade ska ha klocka. Exempelvis om du ska göra PWM så måste du ge klocka både till GPIO, GPIO_AF och timern.

Är du säker på att det är rätt pinne? Har du skrivit rätt Port-namn i koden?

Du har säkert skrivit fel. Dubbelkolla. Visa för någon annan.

Har du kopplat alternate function till pinnen?

AF till pinnen kopplas med funktionen GPIO_PinAFConfig(GPIO_PORT,GPIO_PinSource,GPIO_AF). Info finns i filen GPIO.c.

Har du kört din init-funktion?

Nej, det har du inte. Kolla igen.

Har du enablat din peripheral?

Detta gör man med Periph_CMD(Enable) (t.ex. TIM_Cmd(TIMx,ENABLE), SPI_Cmd(SPIx,ENABLE) eller USART_Cmd(USARTx,ENABLE)).

Min PWM funkar inte!

Notera att för vissa timers (TIM1, TIM8, TIM15-17 på STM32F1xx och endast TIM1,8 på F4) så måste man anropa en extra funktion som sätter igång PWM. Detta görs med funktionen TIM_CtrlPWMOutputs(TIMx,ENABLE). Denna funktion kan också användas för att stänga av PWM på pinnen (vilket kan vara smidigt ibland)

Kontrollera även feldatabladet, t.ex. på TIM1, utgångskomparator kanal 1 på STM32F100 så krockar timern och UART1 vilket gör att timern inte kan användas om uarten används.

Min pinne funkar inte!

Är din pinne PB3, PB4, PA13, PA14 eller PA15? Detta är JTAG/SWD-pinnarna och dom är som standard denna funktion och INTE GPIO. Dessa måste remappas innan dom kan användas som GPIO. Detta görs med funktionen GPIO_PinRemapConfig(GPIO_Remap_SWJ_xxx,ENABLE), där xxx finns som följande options: NoJRST som låser upp PB4, JTAGDisable som låser upp PB3, PB4 och PA15 samt Disable som låser upp alla pinnar (ja, det ser skumt ut när man anropar GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE), men det är korrekt).

  • Notera 1: Eftersom GPIO på dessa pinnar är tekniskt sett en Alternate Function så måste AFIO-ha klocka innan man utför remapping.
  • Notera 2: På STM32F4xx så finns det ingen remap alls. Där sköts detta enbart via AF-controllern (men man måste fortfarande välja GPIO som AF i STM32F4xx).

Den hänger sig i interrupt!

Det finns många saker som kan bli fel på interrupt. Det vanligaste är:

  • Inte korrekt initad interruptvektor. Du måste ha den funktion som svarar mot din interruptvektor (där du hamnar vid interrupt). Dessa står i startup-filen (.s-filen). Dessa har typiskt formatet void PERIPH_Handler(){ do_interrupt_stuff } där PERIPH är den peripheral du har.
  • Har du kvitterat (clearat) ditt interrupt? Detta bör göras så tidigt som möjligt i interruptet (men kan göras senare vid avancerade applikationer). Det finns inbyggda funktioner för detta. Exempelvis, för external interrupt (pinne som ger interrupt) heter den EXTI_ClearItPendingBit(EXTI_Linex), där x är vilket nummer. Timer-funktionen är TIM_ClearItPendingBit(xxx).
  • För vissa interrupt måste du också läsa av interruptkällan (typiskt interrupts som flera källor kan trigga, exempelvis EXTI_Line som kan triggas av flera pinnar) och cleara rätt interrupt-källa.
  • Att blinka LEDs eller skriva sätta pinnar kan vara bra att göra i olika steg i ditt interrupt (och ditt program generellt sett). Då ser du hur långt den kommer. Använd LED_SET() som "prob" i programmet.

De_init-funktionen

Många peripherals (t.ex. DMA och I2C) har en de_init-funktion (t.ex. DMA_DeInit(DMA1_Channel4)). Den nollställer alla inställningar på peripheralen och bör köras innan man initierar peripheralen, annars finns det risk att den inte fungerar.

Logikanalysator

Denna är helt ovärderlig vid felsökning av kommunikationsprotokoll. Om du setat mer än 5 minuter med ditt problem med UART/SPI så hämta logikanalysatorn och koppla upp den. Det tar inte många minuter och löser oftast ditt problem. Den är mycket enkelt att få igång. Mer info finns här. Annars är oscilloskopet din vän. Samma där, det tar inte lång tid att ta fram och sätta upp det.

Min timer dummar sig och sätter sig inte till korrekt idle-värde

P.g.a att ST har gjort fel i sitt bibliotek fungerar inte TIM15 kanal2. De extrafunktioner som har med break att göra (t.ex. idle state) är inte korrekt gjorda. I funktionen TIM_OC2Init i stm32f10x_tim.c ska du lägga till TIMx==TIM15 i if-satsen för att tillåta att de avancerade funktionerna kan användas. Det ska se ut som "if((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15))"

Jag lyckas inte trigga min peripheral (t.ex. ADC) på en timer

För att man ska kunna trigga en peripheral med en timer (t.ex. compare match) så måste man sätta igång output compare-modulen hela vägen. Du behöver inte koppla till en fysisk pinne, men allt utom det måste göras, annars fungerar det inte. Ett tips kan vara att trigga interrupt på samma capture/compare för att se om timern kör. Se följande exempelkod (OBS; sätter ej upp ADC helt komplett):

//Setup timer2
TIM_TimeBaseStruct.TIM_Prescaler = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStruct.TIM_Period = SystemCoreClock/freq;
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

//Init output compare, since ADC trigger cannot be done without it. This is not mapped to any output pin
OC_InitStruct.TIM_OCMode = TIM_OCMode_PWM1;
OC_InitStruct.TIM_OutputState = TIM_OutputState_Enable;
OC_InitStruct.TIM_Pulse=SystemCoreClock/(2*freq);
OC_InitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2,&OC_InitStruct);

//Init ADC
ADC_DeInit(ADC1);
ADC_InitStruct.ADC_ContinuousConvMode=DISABLE;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
ADC_InitStruct.ADC_NbrOfChannel = ADC_NOF_CHAN;
ADC_InitStruct.ADC_ScanConvMode = ENABLE;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_Init(ADC1,&ADC_InitStruct);

//Enable external trigger
ADC_ExternalTrigConvCmd(ADC1, ENABLE);

Mitt DMA-Interrupt fungerar inte

Tänk på att aktivera DMA-kanalen innan interruptet aktiveras. T.ex.

DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);

Min I2C fungerar inte

Varför använder du I2C? Det är ju jättedåligt. Vidare är STs I2C väldigt trasig. Den fungerar bäst med DMA. Kolla mer information i Erratan.

Errata sheet - Men jag är säker på att jag har gjort rätt!

Om det inte fungerar och du är helt säker på att du gjort rätt så kan det vara fel på processorn. Kolla därför igenom Erratan för din processor. Man hittar dom på STs hemsida på sidan om din processor (bara sök på artikelnumret) under Design Resources och scrolla ner till errata. Här är några errata för vanligt använda processorer:
STM32F100 Errata
STM32L1 Errata

Parkodning (gristricket)

En vanlig metod för felsökning är s.k. parkodning (eller gristricket som Sternå kallar det efter Askes kollega). Parkodning innebär att man kodar tillsammans med någon eller visar sin kod för någon annan och berättar vad den gör. Många fel upptäcks på detta sätt. Gristricket kommer av att Askes kollega hade en lite plastgris på sin skärm, och när han stötte på ett jobbigt problem så berättade han för grisen hur koden funkade och vad den gjorde, och hittade på så sätt felet. Att berätta om sin kod högt ska inte underskattas.

Peripheralexempel

Med i libbet kommer det massa exempel som är ganska bra och tar upp det mesta. Här följer några skräddassydda och bättre förklarade exempel.

GPIO

Med interrupt

Timer

PWM

UART

Modeller

Det finns massa olika modeller.


Länkar

Vart hittar man datablad? Vart hittar man libbet? Vart köper man dom?