STM32F0利用C語言位域實現仿位帶操作,編寫STM32通用的sys.h,同正點原子的sys.h函數名和功能一致,方面程序移植

一、前言

平常使用STM32F1和F4,程序不少參考的正點原子的教程,代碼裏都包含頭文件他們的sys.h,這裏面主要是實現了stm32的位帶操作,位帶是啥,博主也沒深入研究,但是就是能直接讀寫GPIO口的某一位,例如:

PAout(1) = 1;//GPIOA Pin1 輸出高電平

 if(PAin(2)==1);//判斷GPIOA Pin2 是否爲高電平

用起來清晰直觀,但是最近使用STM32F0,才發現F0沒有位帶,自然正點原子sys.h裏位帶操作就沒法移植到F0了,那是不是沒辦法了呢,百度一下發現可以利用位域來實現仿位帶操作,下面就來寫一個STMF0的sys.h,廢話不多說,直接看下面代碼。 

 

二、代碼實現

代碼分三步,可以新建名爲sys.h的頭文件,放在裏面:

(1)因爲GPIOX->ODR和GPIOX->IDR寄存器都是低16位有效,所以先定義一個結構體位域,大小爲2字節(16位),每一位表示一個IO口。而且要用volatile 防止被編譯器優化,如果不加volatile ,像PAout(0)=1;PAout(1)=1;原本先後置1,可能會被優化成GPIOA->ODR=0x03,一起置1了,這可是致命錯誤,在模擬I2C、SPI時序的時候會出錯。

//定義GPIO結構體位域
typedef struct
{
   volatile unsigned short bit0 : 1;
   volatile unsigned short bit1 : 1;
   volatile unsigned short bit2 : 1;
   volatile unsigned short bit3 : 1;
   volatile unsigned short bit4 : 1;
   volatile unsigned short bit5 : 1;
   volatile unsigned short bit6 : 1;
   volatile unsigned short bit7 : 1;
   volatile unsigned short bit8 : 1;
   volatile unsigned short bit9 : 1;
   volatile unsigned short bit10 : 1;
   volatile unsigned short bit11 : 1;
   volatile unsigned short bit12 : 1;
   volatile unsigned short bit13 : 1;
   volatile unsigned short bit14 : 1;
   volatile unsigned short bit15 : 1;

} GPIO_Bit_TypeDef;

(2)將 寄存器地址&(GPIOX->ODR)和& (GPIOX->IDR)強制轉換爲GPIO_Bit_TypeDef* 指針,並利用宏定義替換爲標識符PORTX_OUT或PORTX_IN

#define PORTA_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOA->ODR)))
#define PORTA_IN     ((GPIO_Bit_TypeDef *)(&(GPIOA->IDR)))
#define PORTB_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOB->ODR)))
#define PORTB_IN     ((GPIO_Bit_TypeDef *)(&(GPIOB->IDR)))
#define PORTC_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOC->ODR)))
#define PORTC_IN     ((GPIO_Bit_TypeDef *)(&(GPIOC->IDR)))
#define PORTD_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOD->ODR)))
#define PORTD_IN     ((GPIO_Bit_TypeDef *)(&(GPIOD->IDR)))
#define PORTE_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOE->ODR)))
#define PORTE_IN     ((GPIO_Bit_TypeDef *)(&(GPIOE->IDR)))
#define PORTF_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOF->ODR)))
#define PORTF_IN     ((GPIO_Bit_TypeDef *)(&(GPIOF->IDR)))
#define PORTG_OUT    ((GPIO_Bit_TypeDef *)(&(GPIOG->ODR)))
#define PORTG_IN     ((GPIO_Bit_TypeDef *)(&(GPIOG->IDR)))

(3)利用宏函數和##字符串拼接,實現IO口操作,函數名稱和正點原子sys.h裏的一致,方便其他程序移植。這裏提一下“##”拼接,這是一種宏的特殊符號,##表示把左右字符串拼接在一起

例如:#define GPIO(io) GPIO##io  則 GPIO(A) 會被替換成 GPIOA

在下面這裏如果輸入n爲4,就會變成 bit4,n爲8,就會變成bit8

#define PAout(n) (PORTA_OUT->bit##n)
#define PAin(n)  (PORTA_IN->bit##n)
#define PBout(n) (PORTB_OUT->bit##n)
#define PBin(n)  (PORTB_IN->bit##n)
#define PCout(n) (PORTC_OUT->bit##n)
#define PCin(n)  (PORTC_IN->bit##n)
#define PDout(n) (PORTD_OUT->bit##n)
#define PDin(n)  (PORTD_IN->bit##n)
#define PEout(n) (PORTE_OUT->bit##n)
#define PEin(n)  (PORTE_IN->bit##n)
#define PFout(n) (PORTF_OUT->bit##n)
#define PFin(n)  (PORTF_IN->bit##n)
#define PGout(n) (PORTG_OUT->bit##n)
#define PGin(n)  (PORTG_IN->bit##n)

三、例程

實際使用起來就很簡單了,和在F1、F4上的一樣,以點燈爲例,初始化GPIO後,直接賦值就行了

#define LED PAout(4)
void LED_Init(void);

//點燈測試
int main(void)
{
	LED_Init();
	delay_init();
  while (1)
  {
		LED=1;
		delay_ms(500);
		LED=0;
		delay_ms(500);
  }
}

void LED_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);	 //使能PA端口時鐘	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 		 //輸出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度爲50MHz
 GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;//推輓
 GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_NOPULL;//無上下拉
 GPIO_Init(GPIOA, &GPIO_InitStructure);	
	//
 GPIO_SetBits(GPIOA,GPIO_Pin_4);						 //PA.4 輸出高

}
 

感謝該博主提供的思路http://www.cnblogs.com/endlesscoding/p/7429743.html 

 

四、例程下載

打包上面的例程,sys.h在Driver文件夾裏

點擊下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章