0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于STM32調(diào)用固件庫實現(xiàn)點燈

dytfki8u8yql ? 來源:電子技術(shù)控 ? 2023-04-21 09:45 ? 次閱讀

相信學(xué)過單片機的同學(xué),對于調(diào)庫這個操作都不陌生,大多數(shù)人都是從調(diào)別人的庫階段過來的。

今天看到一個評論說,如果只會調(diào)庫,到了公司后會發(fā)現(xiàn)自己啥都不是。其實這話說的一點也不假,如果只會調(diào)庫的話,你的單片機水平還停留在C語言階段,并不能稱為真正的單片機開發(fā)。

36a1dc56-dfb7-11ed-bfe3-dac502259ad0.png

但我們要有這么一個概念:調(diào)庫是自己編寫的開始,如果上來就給你講寄存器這些,我相信很多初學(xué)者都接收不了、理解不了這寫寄存器到底在干啥??墒牵绻麖恼{(diào)別人庫開始學(xué)習(xí)單片機,我們就會對單片機有個初始概念,對于后面的學(xué)習(xí)非常有幫助。

所以,今天我們就來看一下如何從調(diào)庫工程師成為真正的開發(fā)工程師。

1. 什么是調(diào)庫?

如果你通過機構(gòu)的培訓(xùn)視頻,比如野火的STM32單片機開發(fā)視頻,相信你對于調(diào)庫并不陌生,調(diào)庫其實就是通過調(diào)用別人封裝好的庫函數(shù),來實現(xiàn)自己的某些功能,不同的機構(gòu)封裝出來的庫函數(shù)也有所不同,但基本操作都大同小異。

下面,我們就以STM32調(diào)用固件庫實現(xiàn)點燈為例,給大家進行講解。

首先來看一個我們非常熟悉的結(jié)構(gòu)體:

void LED_GPIO_Config(void)//初始化相關(guān)的GPIO 第2個燈
{
GPIO_InitTypeDef GPIO_InitStruct;
/*第一步:打開外設(shè)的時鐘(RCC寄存器控制)*/
RCC_APB2PeriphClockCmd(LED1_GPIO_CLK|LED2_GPIO_CLK,ENABLE);

/*第二步:配置外設(shè)初始化結(jié)構(gòu)體*/
GPIO_InitStruct.GPIO_Pin = LED1_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//推挽輸出

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;


	

/*第三步:調(diào)用外設(shè)初始化函數(shù),把配置好的結(jié)構(gòu)體成員寫到寄存器里面*/
GPIO_Init(LED1_GPIO_PORT,&GPIO_InitStruct);


	

GPIO_InitStruct.GPIO_Pin = LED2_GPIO_PIN;
GPIO_Init(LED2_GPIO_PORT,&GPIO_InitStruct);
}


	

相信對于學(xué)習(xí)過STM32單片機的同學(xué),對于這個函數(shù)都不陌生。這個函數(shù)其實就是實現(xiàn)了對于一個GPIO的初始化,相信初學(xué)者并沒有思考過我們?yōu)槭裁匆@么初始化呢?這里面的一些函數(shù)都有什么作用呢?他們是在哪個地方被封裝的呢?我們可不可以不按照這個函數(shù)的結(jié)構(gòu)來寫呢?

帶著這些疑問,我們繼續(xù)往更深的層次去探索一下這些東西都是什么意思:

這里面用到了很多的宏定義,我們可以使用右鍵-->go to來向前查詢該宏定義在哪個地方進行定義的,例如我們對時鐘的宏定義LED1_GPIO_CLK 具體如下:

#define LED1_GPIO_CLK         RCC_APB2Periph_GPIOC//時鐘
#define LED1_GPIO_PORT GPIOC //端口
#define LED1_GPIO_PIN GPIO_Pin_2//pin 引腳

#define LED2_GPIO_CLK RCC_APB2Periph_GPIOC//時鐘
#define LED2_GPIO_PORT GPIOC //端口
#define LED2_GPIO_PIN GPIO_Pin_3//pin


	

我們可以看到一些宏定義,例如LED1_GPIO_CLK被宏定義為RCC_APB2Periph_GPIOC,這里的RCC_APB2Periph_GPIOC就是官方固件庫中定義的時鐘,如果你想繼續(xù)研究RCC_APB2Periph_GPIOC代表什么意思,我們可以繼續(xù)右鍵-->go to

36bc596e-dfb7-11ed-bfe3-dac502259ad0.png

我們發(fā)現(xiàn)依然是宏定義,這里將RCC_APB2Periph_GPIOC宏定義成了((uint32_t)0x00000010),如果你想繼續(xù)了解((uint32_t)0x00000010)代表什么的話那就需要查看STM32的芯片手冊了,我們這里做一下簡單的講解。

關(guān)于GPIO的需要用到的寄存器如下:

36ea68a4-dfb7-11ed-bfe3-dac502259ad0.png

我們將0x10轉(zhuǎn)換為2進制為:1 0000我們可以看到第四位為1,其他位為0,查看芯片手冊可以發(fā)現(xiàn)第四位解釋如下:

36fc65b8-dfb7-11ed-bfe3-dac502259ad0.png

發(fā)現(xiàn)這句話其實就是在使能I/O端時鐘C,和我們的使用是相同的。到這里我們就知道了從封裝的庫到底層寄存器中間經(jīng)過了什么。當然,這只是一個簡單的例子,實際會比此復(fù)雜很多。

2. 如何不調(diào)庫點亮一個LED?

通過固件庫我們可以看到如果想要控制一個GPIO大概需要以下幾步操作:

  1. 打開GOIO端口的時鐘

  2. .配置IO口為輸出(控制CRL寄存器)

  3. 配置ODR寄存器

知道了需要進行的操作,下一步我們就可以開始通過寄存器操作來控制一個LED了。具體代碼,這里直接貼出來了,大家可以自己進行分析。


	

#define rRCCAHB1CLKEN *((volatile unsigned long *)0x40023830)

#define rGPIOF_MODER *((volatile unsigned long *)0x40021400) #define rGPIOF_OTYPER *((volatile unsigned long *)0x40021404) #define rGPIOF_OSPEEDR *((volatile unsigned long *)0x40021408) #define rGPIOF_IDR *((volatile unsigned long *)0x40021410) #define rGPIOF_ODR *((volatile unsigned long *)0x40021414)

#define rGPIOE_MODER *((volatile unsigned long *)0x40021000) #define rGPIOE_OTYPER *((volatile unsigned long *)0x40021004) #define rGPIOE_OSPEEDR *((volatile unsigned long *)0x40021008) #define rGPIOE_IDR *((volatile unsigned long *)0x40021010) #define rGPIOE_ODR *((volatile unsigned long *)0x40021014)

#define rGPIOA_MODER *((volatile unsigned long *)0x40020000) #define rGPIOA_OTYPER *((volatile unsigned long *)0x40020004) #define rGPIOA_OSPEEDR *((volatile unsigned long *)0x40020008) #define rGPIOA_IDR *((volatile unsigned long *)0x40020010) #define rGPIOA_ODR *((volatile unsigned long *)0x40020014) void key_init() {

rRCCAHB1CLKEN |= 1 | (1 << 1);

rGPIOA_MODER&=~(1|(1<<1));

rGPIOF_OSPEEDR &= ~(1 | (1 << 1) );

rGPIOE_MODER&= ~(0x3f<<4);

rGPIOE_MODER &= ~(0x3f<<4); } void led_init() {

rRCCAHB1CLKEN |= (1 << 5) | (1 << 4);

rGPIOF_MODER &= ~((0x3 << 18) | (0x3 << 20)); rGPIOF_MODER |= (1 << 18) | (1 << 20);

rGPIOF_OTYPER &= ~( (1 << 9) | (1 << 10));

rGPIOF_OSPEEDR &= ~((0x3 << 18) | (0x3 << 20) );

rGPIOF_ODR |= (1 << 9 | 1 << 10) ;

rGPIOE_MODER &= ~((0X3 << 26) | (0X3 << 28) ); rGPIOE_MODER |= (1 << 26) | (1 << 28);

rGPIOE_OTYPER &= ~( (1 << 13) | (1 << 14));

rGPIOE_OSPEEDR &= ~((0x3 << 26) | (0x3 << 28) );

rGPIOE_ODR |= (1 << 13 | 1 << 14) ;

}

void delay(int i) { int v = i; while(v–); }

void led_on(int i) { if (i == 0) { rGPIOF_ODR &= ~(1 << 9); rGPIOF_ODR |= 1 << 10;

rGPIOE_ODR |= (1 << 13) | (1 << 14); } else if (i == 1) { rGPIOF_ODR |= (1 << 9); rGPIOF_ODR &= ~(1 << 10);

rGPIOE_ODR |= (1 << 13) | (1 << 14);

} else if (i == 2) { rGPIOF_ODR |= (1 << 9) | (1 << 10);

rGPIOE_ODR &= ~(1 << 13); rGPIOE_ODR |= 1 << 14; } else if (i == 3) { rGPIOF_ODR |= (1 << 9) | (1 << 10);

rGPIOE_ODR &= ~(1 << 14); rGPIOE_ODR |= 1 << 13; } }

int main() { int i = 0; led_init(); key_init(); while(1) {

if(!(rGPIOA_IDR&1)) { delay(50);//消抖 if(!(rGPIOA_IDR&1)) { led_on(0); } } else { rGPIOF_ODR |= 1 << 9;//μ??e } if(!(rGPIOE_IDR&(1<<2))) { delay(50); if(!(rGPIOE_IDR&(1<<2))) { led_on(1); } } else { rGPIOF_ODR |= 1 << 10; } if(!(rGPIOE_IDR&(1<<3))) { delay(50); if(!(rGPIOE_IDR&(1<<3))) { led_on(2); } } else { rGPIOE_ODR |= 1 << 13; } if(!(rGPIOE_IDR&(1<<4))) { delay(50); if(!(rGPIOE_IDR&(1<<4))) { led_on(3); } } else { rGPIOE_ODR |= 1 << 14; }

} }

上面的代碼實現(xiàn)的功能是通過循環(huán)掃描判斷按鍵是否被按下,如果按鍵被按下則對LED引腳輸出低電平從而點亮LED燈,這里用了四個按鍵和四個LED,方便大家理解之間的不同,引腳的定義如下:

LED的引腳定義為:
LED0 ->PF9
LED1 -> PF10
LED2-> PE13
LED3 -> PE14

按鍵引腳定義為:
KEY0–> PA0
KEY1–> PE2
KEY2–> PE3
KEY3–> PE4

具體每個寄存器代表什么意思,大家可以查看STM32的官方手冊,里面有詳細的介紹。沒有手冊的話,可以看下面這篇文章,里面有常用的寄存器:
https://www.cnblogs.com/jzcn/p/15775328.html

3. 調(diào)庫與不調(diào)庫的區(qū)別

說到這兩者的區(qū)別,也是我寫這篇文章的主要意圖,相信你打開這篇文章絕對不是來看不調(diào)庫是如何開發(fā)的,而是來看調(diào)庫開發(fā)和不調(diào)庫開發(fā)具體有哪些區(qū)別,為什么有現(xiàn)成的庫不用,非要自己去查寄存器,自己進行開發(fā)。

從應(yīng)用角度講,寄存器相對來說是屬于更底層的,類似于驅(qū)動層,而固件庫則類似通過將寄存器封裝之后的應(yīng)用層。相比之下,固件庫更像是包裝好給用戶的產(chǎn)品一樣,只需要我們使用就行了,讓封裝自己和寄存器打交道,而使用寄存器在使用時必須要清楚自己要操作那個一個寄存器,就很復(fù)雜,需要了解清楚寄存器的底層配置。

如果你學(xué)習(xí)過Linux的話,想必你對分層的思想是有所了解的。雖然在單片機中分層思想的應(yīng)用和Linux中的分層不太一樣,但也都是大同小異的。

STM32標準外設(shè)庫之前的版本,也稱固件函數(shù)庫或簡稱固件庫,是?個固件函數(shù)包,它由程序、數(shù)據(jù)結(jié)構(gòu)和宏組成,包括了微控制器所有外設(shè)的性能特征。

該函數(shù)庫還包括每?個外設(shè)的驅(qū)動描述和應(yīng)用實例,為開發(fā)者訪問底層硬件提供了?個中間API,通過使用固件函數(shù)庫,無需深入掌握底層硬件細節(jié),開發(fā)者就可以輕松應(yīng)?每?個外設(shè)。

因此,使?固態(tài)函數(shù)庫可以大大減少用戶的程序編寫時間,進而降低開發(fā)成本。每個外設(shè)驅(qū)動都由?組函數(shù)組成,這組函數(shù)覆蓋了該外設(shè)所有功能。每個器件的開發(fā)都由?個通?API驅(qū)動,API對該驅(qū)動程序的結(jié)構(gòu),函數(shù)和參數(shù)名稱都進?了標準化。

這樣的操作既有好處又有壞處,對于毫無基礎(chǔ)的人來說它可以使我們的控制更加簡單,上手更容易,但是他也會造成我們接觸不到單片機的底層操作,可能你使用單片機干過很多的事,做過很多的項目,但是對于單片機的運行邏輯依然不清楚。

從專業(yè)角度來講,由于寄存器更底層,更需要用戶了解基本構(gòu)成以及底層配置,所以說操作寄存器相對于固件庫顯得更加專業(yè),相比之下,直接操作固件庫不需要了解那么多甚至不了解就可以直接開發(fā),并不需要太多專業(yè)知識。

通過上面的分析,我們可以總結(jié)出他們的優(yōu)缺點:

  • 固件庫優(yōu)點:可以直接應(yīng)用,操作更方便,開發(fā)迅速,適合新手入門。

  • 固件庫缺點:因為操作固件庫,本質(zhì)上也會對寄存器的操作,因為要通過封裝這一中間商,所以執(zhí)行速度要比直接操作寄存器更慢,但沒有寄存器移植那么方便。

所以我們可以從固件庫入門,之后再慢慢深入了解寄存器,了解相關(guān)知識,在我看來,了解更多底層的東西是有利無害的,更利于提升自己,可以懶,但是不能不會。

4. 為什么要操作寄存器?

回歸我們的中心,講了這么多我們到底該如何學(xué)習(xí)單片機呢?詳細這個問題在互聯(lián)網(wǎng)上都已經(jīng)被談爛了。對于初學(xué)者應(yīng)該如何入門應(yīng)該學(xué)習(xí)哪些東西今天這篇文章我就不再討論了,今天要討論的內(nèi)容是如果你已經(jīng)入門了,也已經(jīng)通過操作固件庫做了很多的東西,下一步你應(yīng)該學(xué)習(xí)哪些東西。

如果你已經(jīng)使用單片機做了很多的實驗,比如什么ADC采集、PWM波輸出這些操作你都用過了,并且感覺單片機你已經(jīng)玩的爐火純青了,那么下面的東西對你應(yīng)該很有用。

還有一點需要強調(diào)一下,如果未來你并不打算做單片機相關(guān)的工作的話,那么下面的東西你可以量力而行,可以作為了解的內(nèi)容,并不用深入的了解。

大家學(xué)習(xí)51單片機的時候,是不是經(jīng)常進行一些寄存器操作,為什么我們在32中就很少見到這些直接對寄存器進行操作呢?那是因為32的寄存器相比于51單片機要復(fù)雜很多,比如一個GPIO的操作可能就和很多的寄存器有關(guān),我們很難通過一句話就可以控制一個GPIO,當然不這么干不代表不能這么干。

如果你接觸到單片機的高級開發(fā)(當然沒有這么一說,你可以理解成用單片機做一些產(chǎn)品)那么你的開發(fā)就會遇到瓶頸,從而限制你的開發(fā),這也是很多單片機開發(fā)要求你一定要會寄存器操作。

對于經(jīng)過系統(tǒng)培訓(xùn)的開發(fā)者,單片機(MCU)或SoC的驅(qū)動開發(fā),不管是使用各種庫還是直接上寄存器,都不成問題。

HAL庫函數(shù)或者固件庫都是ST開發(fā)的,也是人寫出來的代碼,既然是代碼,那就有存在BUG的可能,而且像這些經(jīng)過ST調(diào)試過的代碼,更可能隱藏深層問題,這些都需要通過修改寄存器配置來調(diào)試定位。

所以,這就可能在你的代碼里埋下了更深的炸彈,而且這些炸彈是埋藏的非常深的(一般的小bug是不會有的,畢竟那么多人使用)而這些bug一旦復(fù)現(xiàn),你就會不知所措,完全不知道從何查起。

而且一般公司的MCU都不是你平時學(xué)的這些單片機,而是一些工業(yè)級的MCU,所以你可以想一想如果你一直使用的都是STM32的固件庫進行的開發(fā),從來沒接觸過寄存器操作,或者根本都不知道怎么看芯片手冊,怎么操作寄存器,那么你怎么保證 你們公司使用的MCU你就一定會操作呢?

所以,對于STM32或者51這類單片機的定位,你就把它當成學(xué)習(xí)使用的,你要通過這類簡單的、有豐富資料的單片機去入門、去學(xué)習(xí)。當你的學(xué)習(xí)內(nèi)容達到一定程度后,就一定會接觸到寄存器這些操作。

說實話,寄存器操作也只是工作的基礎(chǔ),最重要的是舉一反三,通過一個單片機學(xué)習(xí)到所有MCU操作的本質(zhì),這樣才能更好的在工作中使用,而不受單片機(MCU)型號的限制。

5. 結(jié)語

對于單片機的學(xué)習(xí)我們可以使用單片機的固件庫入門,初步了解單片機操作的步驟,可以先不接觸寄存器,等到固件庫使用的非常熟悉之后可以轉(zhuǎn)戰(zhàn)寄存器了。

對于寄存器操作絕不是點個小燈就完了,你需要做的是知道如何查看芯片手冊,知道固件庫里的每個宏定義或者函數(shù)這么寫的依據(jù)是什么?如果讓你來寫一個固件庫你會怎么寫?

當你的水平能夠達到對STM32的寄存器操作非常6的話,你可以嘗試幾款工業(yè)級的MCU,例如工業(yè)非常常用的TC397,這個MCU在車載行業(yè)用的非常多,可以嘗試一下,不過治療可能不太好找,如果遇到問題的話就需要自己琢磨了,這也是一種進步。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6030

    文章

    44489

    瀏覽量

    631968
  • mcu
    mcu
    +關(guān)注

    關(guān)注

    146

    文章

    16885

    瀏覽量

    349916
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5294

    瀏覽量

    119814
  • STM32
    +關(guān)注

    關(guān)注

    2264

    文章

    10854

    瀏覽量

    354289
  • GPIO
    +關(guān)注

    關(guān)注

    16

    文章

    1188

    瀏覽量

    51832

原文標題:單片機低水平?只會調(diào)庫和復(fù)制別人的代碼

文章出處:【微信號:電子技術(shù)控,微信公眾號:電子技術(shù)控】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    STM32固件調(diào)用 精選資料分享

    文章目錄固件文件解讀善于使用固件用戶手冊楊桃32學(xué)習(xí)筆記,本文圖片文字皆為轉(zhuǎn)述固件文件解讀
    發(fā)表于 08-24 06:26

    STM32固件使用手冊【中文】

    32位基于ARM微控制器STM32F101xx與STM32F103xx固件函數(shù)
    發(fā)表于 10-30 17:55 ?45次下載

    STM32移植過程詳解以固件

    STM32 固件移植的方法及簡單介紹。。
    發(fā)表于 12-17 13:54 ?18次下載

    STM32固件V3.5.0使用

    本文主要介紹了STM32固件V3.5.0的使用方法,比較適合初學(xué)STM32的人。
    發(fā)表于 06-17 17:24 ?10次下載

    如何用STM32固件來建立工程

    這節(jié)課主要給朋友們講解以下這幾個方面的內(nèi)容: 1:如何學(xué)習(xí) STM32 單片機; 2:STM32 的開發(fā)是用固件,還是用直接操作寄存器; 3:ST
    發(fā)表于 11-11 17:17 ?29次下載

    STM32固件說明(中文版)

    STM32固件說明(中文版)
    發(fā)表于 02-07 21:06 ?0次下載

    STM32固件中文使用手冊

    STM32固件中文使用手冊
    發(fā)表于 09-20 08:49 ?157次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>固件</b><b class='flag-5'>庫</b>中文使用手冊

    stm32固件,里面很多函數(shù)

    stm32固件,里面很多函數(shù)
    發(fā)表于 09-20 12:50 ?19次下載
    <b class='flag-5'>stm32</b><b class='flag-5'>固件</b><b class='flag-5'>庫</b>,里面很多函數(shù)

    STM32固件

    STM32固件
    發(fā)表于 09-29 14:53 ?81次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>固件</b><b class='flag-5'>庫</b>

    STM32固件例程

    STM32固件例程分享,有需要的小伙伴看下
    發(fā)表于 12-06 16:41 ?57次下載

    STM32官方固件

    1 基于標準外設(shè)的軟件開發(fā)1.1 STM32標準外設(shè)概述STM32標準外設(shè)之前的版本也稱固件
    發(fā)表于 11-22 11:21 ?64次下載
    <b class='flag-5'>STM32</b>官方<b class='flag-5'>固件</b><b class='flag-5'>庫</b>

    STM32固件認識+固件模板點燈

    一、stm32固件的認識1-匯編編寫的啟動文件startup_stm32f10xhd.s:設(shè)置堆棧指針、設(shè)置Pc指針、初始化中斷向量表、配置系統(tǒng)時鐘、······2-時鐘配置文件sy
    發(fā)表于 11-23 16:21 ?8次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>固件</b><b class='flag-5'>庫</b>認識+<b class='flag-5'>固件</b><b class='flag-5'>庫</b>模板<b class='flag-5'>點燈</b>

    STM32實現(xiàn)點燈固件方式)

    STM32實現(xiàn)點燈固件方式)
    發(fā)表于 12-05 11:06 ?11次下載
    <b class='flag-5'>STM32</b>上<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>點燈</b>(<b class='flag-5'>固件</b><b class='flag-5'>庫</b>方式)

    STM32固件的下載

    STM32固件的下載
    發(fā)表于 12-07 12:21 ?84次下載
    <b class='flag-5'>STM32</b><b class='flag-5'>固件</b><b class='flag-5'>庫</b>的下載

    STM32固件

    stm32固件分享
    發(fā)表于 06-08 09:09 ?13次下載