Sida 1 av 1

STM32 GPIO med STM32F10x_StdPeriph_Lib_V3.2.0

Postat: 12 april 2010, 20:48:34
av d49l
Hej!

Sitter och försöker få full kläm på GPIO portarna på STM32 tillsammans med standardbiblioteket. Jag behövde dokumentera detta för min egen del så jag tänkte att någon annan också kan ha nytta av detta, samtidigt som det hela kan faktagranskas av någon som kan verifiera korrektheten genom att ha vart där och verkligen gjort det. Nu kommer jag skriva en massa skit och antaganden som inte är 100% verifierat så jag förväntar mig kritik :-)

Först ut till lite allmän information om GPIO portarna...
Man kan ställa in en pinne på en port som ingång eller som utgång. På en utgång kan man även ställa in snabbheten för att optimera för EMC, lägre hastighet är lika med bättre EMC egenskaper. Så om man inte behöver den högsta hastigheten, kan man därmed ställa ner den. Maxhastigheten kan ställas in till 2MHz,10MHz eller 50MHz.

Om man ställer in en pinne till utgång så kan den även drivsteget konfigureras som push pull eller open drain.

Om man ställer in en pinne som ingång kan man välja mellan att ge den ett internt motstånd som drar signalen till jord, eller alternativt till matning. Man kan också låta den vara en vanlig (flytande) digital ingång.

Varje GPIO port har två st 32-bitars konfigurations register. Tillsammans skapar de ett 64-bit konfigurations register. Dessa 64 bitar ger varje pinne ett 4bitars fält för att kunna konfigurera I/O pinnen. Varje port har ju som bekant 16st pinnar, och 16*4 = 64.

Nu till ett kodexempel...

Kod: Markera allt

/* Private variables ---------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure; /* Definition kan användas tillsammans med funktionen GPIO_Init */

/* GPIO configuration ------------------------------------------------------*/
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); /* Enable GPIOE clock. Går att stänga av för att spara ström och förbättra EMC egenskaper men behöver slås på efter reset för att aktivera porten*/

  /* Configure PE.00, PE.01 as output push-pull */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1; /* Vilka pinnar vi vill påverka för denna inställning */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* Vilken typ vi vill ställa in pinnarna till */
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* Högsta arbetshastighet för pinnarna, lägre hastighet bättre EMC egenskaper */
  GPIO_Init(GPIOE, &GPIO_InitStructure); /* Överför och aktivera själva inställningen vi satt upp ovan i GPIO_InitStructure */

Nu till lite dokumentering av exemplet...

Först aktiverar du klockan som driver porten, i detta fall för port E...
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

Låt säga att du även skulle behöva aktivera klockan till ytterligare en port, det gör du på följande sätt (klockorna för både port E och port D startas)...
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOD, ENABLE);

Du väljer vilka pinnar du vill påverka...
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; /* Vilka pinnar vi vill påverka för denna inställning */

Du kan lägga till fler pinnar att konfigurera från samma port likt följande...
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;

De möjliga inställningarna för att välja pinnar ovan, är de följande...
GPIO_Pin_0
GPIO_Pin_1
GPIO_Pin_2
GPIO_Pin_3
GPIO_Pin_4
GPIO_Pin_5
GPIO_Pin_6
GPIO_Pin_7
GPIO_Pin_8
GPIO_Pin_9
GPIO_Pin_10
GPIO_Pin_11
GPIO_Pin_12
GPIO_Pin_13
GPIO_Pin_14
GPIO_Pin_15
GPIO_Pin_All

Raden nedan ställer in typen av ingång utgång...
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

Alternativa inställningar för GPIO_InitStructure.GPIO_Mode är de följande...
GPIO_Mode_AIN // Analog, kan väljas om det är frågan om en ADC ingång.
GPIO_Mode_IN_FLOATING // Input floating, vanlig digital ingång.
GPIO_Mode_IPD // Input-pull-down, ingång med det inbyggda pull-down motståndet.
GPIO_Mode_IPU // Input pull-up, ingång med det inbyggda pull-up motståndet.
GPIO_Mode_Out_OD // Output open-drain, om du enbart vill kunna sänka signalen.
GPIO_Mode_Out_PP // Output push-pull, vanlig digital utgång som sänka och driva.
GPIO_Mode_AF_OD // Alternate function open-drain, kan användas om pinnen skall användas av ett tillbehör.
GPIO_Mode_AF_PP // Alternate function push-pull, kan användas om pinnen skall användas av ett tillbehör.

OBS! Om du konfigurerar en I/O pinne till GPIO_Mode_AF_OD eller GPIO_Mode_AF_PP, så kopplar detta bort dessa pinar från utgångsregistren och kopplar pinnen till utsignalen till tillbehöret i mikrokontrollern. Om vi konfigurerar en GPIO pinne som 'Alternate Function Output' (GPIO_Mode_AF_OD eller GPIO_Mode_AF_PP) men inte har aktiverat tillbehöret, så kommer värdet på utgången inte vara specificerat.

Följande Rad används om du ställer in en utgång, men inte om du ställer in en ingång...
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* Högsta arbetshastighet för ingången */

Alternativa inställningar för GPIO_InitStructure.GPIO_Speed är de följande...
GPIO_Speed_10MHz
GPIO_Speed_2MHz
GPIO_Speed_50MHz

Slutligen har vi raden som aktiverar inställningen för port E...
GPIO_Init(GPIOE, &GPIO_InitStructure); /* Överför och aktivera själva inställningen vi satt upp ovan i GPIO_InitStructure */

Vi kan ändra porten genom att byta bokstaven som anger vilken port man vill konfigurera, likt följande...
GPIO_Init(GPIOC, &GPIO_InitStructure);

Namnen på de möjliga portarna är de följande...
GPIOA
GPIOB
GPIOC
GPIOD
GPIOE
GPIOF
GPIOG


Sen vill man ju kunna ställa in utgångarna de gör man på följande sätt, men först lite dokumentation...
'bit set/reset' registret är ett 32-bitars register. De övre 16 bitarna knutna till varje ports pinnar, lika dant med de undre 16 bitarna.
1:or i de övre bitarna, sätter pinnar till 0. 1:or i de lägre bitarna, sätter pinnar till 1.

Nu till lite exempel...

/* Sätter valda pinnar höga, men lämnar de andra opåverkade */
GPIO_SetBits(GPIOE, GPIO_Pin_0); /* sätter pinne 0 till hög */


Men du kan även sätta fler pinnar höga på följande sätt, vilket även detta lämnar de andra pinnarna opåverkade...
GPIO_SetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_15); /* sätter pinne 0 och 15 till hög */

/* Sätter valda pinnar till låga, men lämnar de andra opåverkade */
GPIO_ResetBits(GPIOE, GPIO_Pin_0); /* sätter pinne 0 till låg */


Men du kan även sätta fler pinnar låga på följande sätt, vilket även detta lämnar de andra pinnarna opåverkade...
GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_1); /* sätter pinne 0 och 1 till låg */

Men det finns även andra sätt att påverka utgångarna på porten än 'bit set/reset' registret
Du kan nämligen ställa in hela porten i ett enda steg...

/* Påverkar hela porten. Välj de pinnar du vill sätta höga */
GPIO_Write(GPIOE, GPIO_Pin_0); /* sätter pinne 0 till hög, och de andra till låga */


Men du kan även ställa in en specifik pinne...
GPIO_WriteBit(GPIOE, GPIO_Pin_0, Bit_SET); /* Sätter pinne 0 till hög */
GPIO_WriteBit(GPIOE, GPIO_Pin_0, Bit_RESET); /* Sätter pinne 0 till låg */


Nu till hur man läser av ingångar på porten...

Du kan läsa av en enskild pinne (som du ställt in till ingång på porten) med följande funktion...
GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3); /* Läser av pinne 3 på porten */

Det finns en massa andra saker att säga om portarna och hur man kan ställa in dem. Men jag stannar här. Du kan till exempel låsa din konfiguration av porten. Vad har man för nytta av den? Kanske någon kan ge ett svar. Man kan även flytta om pinnar på porten. Kanske någon annan kan berätta lite mer om det. Hoppas någon finner glädje av detta. Men kom ihåg, det jag skrivit här behöver nödvändigtvis inte stämma eftersom jag inte är någon expert och inte har testat allt för att se att mina antagande stämmer. Kanske någon annan kan verifiera.

Sen har jag en fundering på användningen av GPIO_Init och GPIO_InitStructure
Hur gör man när man vill ställa in olika typer av funktioner på samma port? använder man detta flera gånger efter varandra eller hur gör man? Det vet jag faktiskt inte riktigt ännu, är det någon som sitter på svaret så lämna gärna en kommentar.

// Daniel

Re: STM32 GPIO med STM32F10x_StdPeriph_Lib_V3.2.0

Postat: 13 april 2010, 20:00:24
av E85
Här är ett exempel på hur jag har satt både in och ut på samma port. Det är precis som du tror...

Kod: Markera allt

  
  /* Configure PA.0 as Output push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Configure PA.1-4 as input with pullup */
  GPIO_InitStructure.GPIO_Pin = GPIO_Row_1 | GPIO_Row_2 | GPIO_Row_3 | GPIO_Row_4;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

Re: STM32 GPIO med STM32F10x_StdPeriph_Lib_V3.2.0

Postat: 13 april 2010, 22:29:02
av d49l
Ok!

En liten detalj bara i ditt exempel, när du konfigurerar ingången. Det går inte ställa hastigheten på en ingång, vilket görs i koden nedan

/* Configure PA.1-4 as input with pullup */
GPIO_InitStructure.GPIO_Pin = GPIO_Row_1 | GPIO_Row_2 | GPIO_Row_3 | GPIO_Row_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);


Kan det rent av vara så att om du råkar göra det i omvänd ordning så slutar koden att göra det den ska. Typ följande skulle bli fel...

/* Configure PA.1-4 as input with pullup */
GPIO_InitStructure.GPIO_Pin = GPIO_Row_1 | GPIO_Row_2 | GPIO_Row_3 | GPIO_Row_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);


Det borde väll resultera i pinnen ställs om som GPIO_Mode_AF_PP ?
Anledningen till det skulle i så fall vara att bitarna i MOD1 och MOD0 kommer att ställas om, så det blir en utgång istället.
Jag har för lite erfarenhet i C-kod för att svära vid att så är fallet.

En spontan fråga är om de 4 översta raderna i dessa exempel genererar någon kod till processorn, eller om det är kompilatorn som tar hand om dem? (med andra andra ord att det bara är sista raden som genererar kod) ?

Re: STM32 GPIO med STM32F10x_StdPeriph_Lib_V3.2.0

Postat: 13 april 2010, 23:05:46
av E85
Ja det har du rätt i men eftersom det är en struct som återanvänds så spelar det värdet ingen roll i det nedre fallet eftersom om det inte hade satts så hade det legat kvar från den övre tilldelningen, dvs 50MHz. Spelar ingen roll hur tilldelningen av värdena i structen är ordnade, nej.

Re: STM32 GPIO med STM32F10x_StdPeriph_Lib_V3.2.0

Postat: 14 april 2010, 03:34:43
av d49l
Ok!

Jag testade det hela på en STM32. Hastighetsinställningen i structen påverkar inget, om man ställer in pinnen som någon typ av ingång i structen. Och det kanske är tur det :-)

Men för att inte göra någon stackare som läster detta förvirrad, så går det hur som helst inte ställa in hastigheten på en ingång. De bitar som rör hastighet måste vara inställda på 00 (MOD1,MOD2 bitarna), vilket inte är en hastighet utan istället gör pinnen till någon form av ingång.

Tackar för hjälpen med att reda ut det hela.