<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 工程師STM32單片機(jī)學(xué)習(xí)基礎(chǔ)手記(2):從勉強(qiáng)看懂一行程序到IO口研究

          工程師STM32單片機(jī)學(xué)習(xí)基礎(chǔ)手記(2):從勉強(qiáng)看懂一行程序到IO口研究

          作者: 時(shí)間:2012-10-22 來源:網(wǎng)絡(luò) 收藏

           強(qiáng)看懂

            繼續(xù)中,先把開發(fā)板自帶一個(gè)例子做了些精簡(jiǎn),以免看得嚇人。。。。
            
            就是這個(gè),讓PORTD上接的4個(gè)LED分別點(diǎn)亮。
            開始代碼
            int main(void)
            {
            Init_All_Periph();
            。。.。。.
            看到這,開始跟蹤,于是又看到了下面的內(nèi)容
            void Init_All_Periph(void)
            {
            RCC_Configuration();
            。。.。。.
            繼續(xù)跟蹤
            void RCC_Configuration(void)
            {
            SystemInit();
            。。.。。.
            這行代碼在system_stm32f10x.c中找到了。
            void SystemInit (void)
            {
            /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
            /* Set HSN bit */
            RCC-》CR |= (uint32_t)0x00000001;
            /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
            #ifndef F10X_CL
            RCC-》CFGR = (uint32_t)0xF8FF0000;
            #else
            RCC-》CFGR = (uint32_t)0xF0FF0000;
            #endif /* F10X_CL */
            /* Reset HSEON, CSSON and PLLON bits */
            RCC-》CR = (uint32_t)0xFEF6FFFF;
            /* Reset HSEBYP bit */
            RCC-》CR = (uint32_t)0xFFFBFFFF;
            /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
            RCC-》CFGR = (uint32_t)0xFF80FFFF;
            #ifndef F10X_CL
            /* Disable all interrupts and clear pending bits */
            RCC-》CIR = 0x009F0000;
            #else
            /* Reset PLL2ON and PLL3ON bits */
            RCC-》CR = (uint32_t)0xEBFFFFFF;
            /* Disable all interrupts and clear pending bits */
            RCC-》CIR = 0x00FF0000;
            /* Reset CFGR2 register */
            RCC-》CFGR2 = 0x00000000;
            #endif /* STM32F10X_CL */
            /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
            /* Configure the Flash Latency cycles and enable prefetch buffer */
            SetSysClock();
            }
            這一長(zhǎng)串的又是什么,如何來用呢?看來,偷懶是不成的了,只能回過頭去STM32的時(shí)鐘構(gòu)成了。
            相當(dāng)?shù)膹?fù)雜。

          本文引用地址:http://www.ex-cimer.com/article/170756.htm

           系統(tǒng)的時(shí)鐘可以有3個(gè)來源:內(nèi)部時(shí)鐘HSI,外部時(shí)鐘HSE,或者PLL(鎖相環(huán)模塊)的輸出。它們由RCC_CFGR寄存器中的SW來選擇。
            SW(1:0):系統(tǒng)時(shí)鐘切換
            由軟件置’1’或清’0’來選擇系統(tǒng)時(shí)鐘源。 在從停止或待機(jī)模式中返回時(shí)或直接或間接作為系統(tǒng)時(shí)鐘的HSE出現(xiàn)故障時(shí),由硬件強(qiáng)制選擇HSI作為系統(tǒng)時(shí)鐘(如果時(shí)鐘安全系統(tǒng)已經(jīng)啟動(dòng))
            00:HSI作為系統(tǒng)時(shí)鐘;
            01:HSE作為系統(tǒng)時(shí)鐘;
            10:PLL輸出作為系統(tǒng)時(shí)鐘;
            11:不可用。
            ////////////////////////////////////////////////////////////////////
            PLL的輸出直接送到USB模塊,經(jīng)過適當(dāng)?shù)姆诸l后得到48M的頻率供USB模塊使用。
            系統(tǒng)時(shí)鐘的一路被直接送到I2S模塊;另一路經(jīng)過AHB分頻后送出,送往各個(gè)系統(tǒng),其中直接送往SDI,F(xiàn)MSC,AHB總線;8分頻后作為系統(tǒng)定時(shí)器時(shí)鐘;經(jīng)過APB1分頻分別控制PLK1、定時(shí)器TIM2~TIM7;經(jīng)過APB2分頻分別控制PLK2、定時(shí)器TIM1~TIM8、再經(jīng)分頻控制ADC;
            由此可知,STM32F10x芯片的時(shí)鐘比之于51、AVR、PIC等8位機(jī)要復(fù)雜復(fù)多,因此,我們立足于對(duì)著芯片手冊(cè)來解讀,力求知道這些代碼如何使用,為何這么樣使用,如果自己要改,可以修改哪些部分,以便自己使用時(shí)可以得心應(yīng)手。
            單步執(zhí)行,看一看哪些代碼被執(zhí)行了。
            /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
            /* Set HSN bit */
            RCC-》CR |= (uint32_t)0x00000001;
            120S52109-1.jpg
            這是RCC_CR寄存器,由圖可見,HSN是其bit 0位。
            HSION:內(nèi)部高速時(shí)鐘使能
            由軟件置’1’或清零。
            當(dāng)從待機(jī)和停止模式返回或用作系統(tǒng)時(shí)鐘的外部4-25MHz時(shí)鐘發(fā)生故障時(shí),該位由硬件置’1’來啟動(dòng)內(nèi)部8MHz的RC振蕩器。當(dāng)內(nèi)部8MHz時(shí)鐘被直接或間接地用作或被選擇將要作為系統(tǒng)時(shí)鐘時(shí),該位不能被清零。
            0:內(nèi)部8MHz時(shí)鐘關(guān)閉;
            1:內(nèi)部8MHz時(shí)鐘開啟。
            ///////////////////////////////////////////////////////////////////////
            /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
            #ifndef STM32F10X_CL
            RCC-》CFGR = (uint32_t)0xF8FF0000;
            點(diǎn)擊看大圖
            這是RCC_CFGR寄存器
            該行程序清零了MC0[2:0]這三位,和ADCPRE[1:0],ppre2[2:0],PPRE1[2:0],HPRE[3:0],SWS[1:0]和SW[1:0]這16位。
            /*
            MCO: 微控制器時(shí)鐘輸出,由軟件置’1’或清零。
            0xx:沒有時(shí)鐘輸出;
            100:系統(tǒng)時(shí)鐘(SYSCLK)輸出;
            101:內(nèi)部8MHz的RC振蕩器時(shí)鐘輸出;
            110:外部4-25MHz振蕩器時(shí)鐘輸出;
            111:PLL時(shí)鐘2分頻后輸出。
            */
            /* Reset HSEON, CSSON and PLLON bits */
            RCC-》CR = (uint32_t)0xFEF6FFFF;
            清零了PLLON,HSEBYP,HSERDY這3位。
            /* Reset HSEBYP bit */
            RCC-》CR = (uint32_t)0xFFFBFFFF;
            清零了HSEBYP位 ///???為什么不一次寫??
            HSEBYP:外部高速時(shí)鐘旁路,在調(diào)試模式下由軟件置’1’或清零來旁路外部晶體振蕩器。只有在外部4-25MHz振蕩器關(guān)閉的情況下,才能寫入該位。
            0:外部4-25MHz振蕩器沒有旁路;
            1:外部4-25MHz外部晶體振蕩器被旁路。
            所以要先清HSEON位,再清該位。
            /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
            RCC-》CFGR = (uint32_t)0xFF80FFFF;
            清零了:USBPRE,PLLMUL,PLLXTPR,PLLSRC共7位
            /* Disable all interrupts and clear pending bits */
            RCC-》CIR = 0x009F0000;
            ////這個(gè)暫不解讀
            SetSysClock();
           跟蹤進(jìn)入該函數(shù),可見一連串的條件編譯:


            單步運(yùn)行,執(zhí)行的是:
            #elif defined SYSCLK_FREQ_72MHz
            SetSysClockTo72();
            為何執(zhí)行該行呢,找到SYSCLK_PREQ_**的相關(guān)定義,如下圖所示。
            
            這樣就得到了我們所要的一個(gè)結(jié)論:如果要更改系統(tǒng)工作頻率,只需要在這里更改就可以了。
            可以繼續(xù)跟蹤進(jìn)入這個(gè)函數(shù)來觀察如何將工作頻率設(shè)定為72MHz的。
            static void SetSysClockTo72(void)
            {
            __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
            /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
            /* Enable HSE */
            RCC-》CR |= ((uint32_t)RCC_CR_HSEON);
            //開啟HSE
            /* Wait till HSE is ready and if Time out is reached exit */
            do
            {
            HSEStatus = RCC-》CR RCC_CR_HSERDY;
            StartUpCounter++;
            } while((HSEStatus == 0) (StartUpCounter != HSEStartUp_TimeOut));
            //等待HSE確實(shí)可用,這有個(gè)標(biāo)志,即RCC_CR寄存器中的HSERDY位(bit 17),這個(gè)等待不會(huì)無限長(zhǎng),有個(gè)超時(shí)策略,即每循環(huán)一次計(jì)數(shù)器加1,如果計(jì)數(shù)的次數(shù)超過HSEStartUp_TimeOut,就退出循環(huán),而這個(gè)HSEStartUp_TimeOut在stm32f10x.h中定義,
            #define HSEStartUp_TimeOut ((uint16_t)0x0500) /*!《 Time out for HSE start up */
            ///////////////////////////////////////////////////////////////////////////////////////////////
            if ((RCC-》CR RCC_CR_HSERDY) != RESET)
            {
            HSEStatus = (uint32_t)0x01;
            }
            else
            {
            HSEStatus = (uint32_t)0x00;
            }
            ///再次判斷HSERDY標(biāo)志位,并據(jù)此給HSEStatus變量賦值。
            if (HSEStatus == (uint32_t)0x01)
            {
            /* Enable Prefetch Buffer */
            FLASH-》ACR |= FLASH_ACR_PRFTBE;
            /* Flash 2 wait state */
            FLASH-》ACR = (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
            FLASH-》ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
            /* HCLK = SYSCLK */
            RCC-》CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
            //找到定義: #define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000) /*!《 SYSCLK not divided */
            /* PCLK2 = HCLK */
            RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
            //找到定義:#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000) /*!《 HCLK not divided */
            /* PCLK1 = HCLK */
            RCC-》CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
            //找到定義:#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400) /*!《 HCLK divided by 2 */
            #ifdef STM32F10X_CL
            ……
            #else
            /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
            RCC-》CFGR = (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
            RCC_CFGR_PLLMULL));
            RCC-》CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
            #endif /* STM32F10X_CL */
            //以上是設(shè)定PLL的倍頻系數(shù)為9,也就是說,這個(gè)72M是在外部晶振為8M時(shí)得到的。
            /* Enable PLL */
            RCC-》CR |= RCC_CR_PLLON;
            /* Wait till PLL is ready */
            while((RCC-》CR RCC_CR_PLLRDY) == 0)
            {
            }
            /* Select PLL as system clock source */
            RCC-》CFGR = (uint32_t)((uint32_t)~(RCC_CFGR_SW));
            RCC-》CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
            /* Wait till PLL is used as system clock source */
            while ((RCC-》CFGR (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
            {
            }
            }
            else
            { /* If HSE fails to start-up, the application will have wrong clock
            configuration. User can add here some code to deal with this error */
            /* Go to infinite loop */
            while (1)
            {
            }
            }
            }

            至此,我們可以歸納幾條:
            (1) 時(shí)鐘源有3個(gè)
           ?。?) 開機(jī)時(shí)默認(rèn)是HSI起作用,可以配置為所要求的任意一個(gè)時(shí)鐘
           ?。?) 配置時(shí)必須按一定的順序來打開或都關(guān)閉一些位,并且各時(shí)鐘起作用有一定的時(shí)間,因此要利用芯片內(nèi)部的標(biāo)志位來判斷是否可以執(zhí)行下一步。
           ?。?) 如果外部時(shí)鐘、PLL輸出失效,系統(tǒng)可以自動(dòng)回復(fù)到HSI(開啟時(shí)鐘安全系統(tǒng))
            (5) HSI的頻率準(zhǔn)確度可以達(dá)到+/- 1%,如果有必要時(shí),還可以用程序來調(diào)整這個(gè)頻率,可調(diào)的范圍大致在200KHz左右。
            最后讓我們來感受一下勞動(dòng)的果實(shí)吧--試著改改頻率看有何反應(yīng)。
            為查看更改后的效果,先記錄更改前的數(shù)據(jù)。將調(diào)試切換到仿真,在第一條:
            Delay(0xAFFFF);
            指令執(zhí)行前后,分別記錄下Status和Sec
            Status:2507 3606995
            Sec:0.00022749 0.05028982
            將振蕩頻率更改為36MHz,即
            。。.
            #define SYSCLK_FREQ_36MHz 36000000 //去掉該行的注釋
            /* #define SYSCLK_FREQ_48MHz 48000000 */
            /* #define SYSCLK_FREQ_56MHz 56000000 */
            /*#define SYSCLK_FREQ_72MHz 72000000*/ //將該行加上注釋
            再次運(yùn)行,結(jié)果如下:
            Status:2506 3606994
            Sec:0.00008478 0.10036276
            基本上是延時(shí)時(shí)間長(zhǎng)了一倍。改成硬件仿真,將代碼寫入板子,可以看到LED閃爍的頻率明顯變慢了。

          IO

            前面的例子研究了時(shí)鐘,接下來就來了解一下引腳的情況
            Main.c中,有關(guān)I/O口的配置代碼如下:
            void GPIO_Configuration(void)
            {
            GPIO_InitTypeDef GPIO_InitStructure;
            /* Configure IO connected to LD1, LD2, LD3 and LD4 leds *********************/
            GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_Init(GPIOD, GPIO_InitStructure);
            這幾行代碼是將GPIOD的第8,9,10和11引腳配置成輸出,并且還可以設(shè)定輸出引腳的速度(驅(qū)動(dòng)能力?),這里設(shè)定為 50MHz,這應(yīng)該是常用的,還有可以設(shè)置為2MHz的。那么如何將引腳設(shè)置成輸入呢?查看電路原理圖,GPIOD.0~GPIO.4是接一個(gè)搖桿的5個(gè)按鈕的,因此,下面嘗試著將它們?cè)O(shè)置成為輸入端。
            GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
            GPIO_Init(GPIOD, GPIO_InitStructure);
            第1行和第3行完全是照抄,第2行那個(gè)GPIO_Mode_IN_FLOATING是在stm32f10x_gpio.h中找到的。
            
            當(dāng)然是因?yàn)檫@里還有GPIO_Mode_Out_PP,所以猜測(cè)應(yīng)該是它了。至于還有其他那么多的符號(hào)就不管了。
            定義完成,編譯完全通過,那就接下來準(zhǔn)備完成下面的代碼了。
            int main(void)
            {
            Init_All_Periph();
            while(1)
            { if( GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_0)) //1
            { GPIO_ResetBits(GPIOD, GPIO_Pin_8);
            }
            else
            { /* Turn on LD1 */
            GPIO_SetBits(GPIOD, GPIO_Pin_8);
            /* Insert delay */
            }
            。。.。。.
            標(biāo)號(hào)為1的行顯然其作用是判斷GPIOD.0引腳是0還是1。這個(gè)函數(shù)是在stm32f10x_gpio.c中找到的。
            uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
            {
            uint8_t bitstatus = 0x00;
            /* Check the parameters */
            assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
            assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
            if ((GPIOx-》IDR GPIO_Pin) != (uint32_t)Bit_RESET)
            {
            bitstatus = (uint8_t)Bit_SET;
            }
            else
            {
            bitstatus = (uint8_t)Bit_RESET;
            }
            return bitstatus;
            }
            雖然程序還有很多符號(hào)看不懂(沒有去查),但憑感覺它應(yīng)該是對(duì)某一個(gè)引腳的狀態(tài)進(jìn)行判斷,因?yàn)檫@個(gè)函數(shù)的類型是uint8_t,估計(jì)stm32沒有bit型函數(shù)(需要驗(yàn)證),所以就用了uint8_t型了),如果是讀的端口的值,應(yīng)該用uint16_t型。這一點(diǎn)在下面也可以得到部分的驗(yàn)證:
            uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
            uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)
            這些函數(shù)是讀引腳及輸出寄存器的數(shù)據(jù)的。

           再次編譯,也是順利通過,依法炮制,將其他三個(gè)引腳輸入控制LED的代碼也寫上,為保險(xiǎn)起見,先用軟件仿真,免得反復(fù)擦寫FLASH(順便說一句,目前還沒有搞定將代碼寫入RAM及從RAM中執(zhí)行)
            點(diǎn)擊看大圖
            進(jìn)入仿真后打開外圍部件接口,單步執(zhí)行,果然如同設(shè)想那樣運(yùn)作了,單擊Pins 0后面的勾,再次運(yùn)行,果然PIN8后面的勾沒了。做到這里,就感覺到用keil的好處了,這塊熟啊,幾乎沒有花時(shí)間在上面,一用就成了。

          單片機(jī)相關(guān)文章:單片機(jī)教程


          單片機(jī)相關(guān)文章:單片機(jī)視頻教程


          單片機(jī)相關(guān)文章:單片機(jī)工作原理


          分頻器相關(guān)文章:分頻器原理
          塵埃粒子計(jì)數(shù)器相關(guān)文章:塵埃粒子計(jì)數(shù)器原理
          晶振相關(guān)文章:晶振原理
          鎖相環(huán)相關(guān)文章:鎖相環(huán)原理

          上一頁 1 2 下一頁

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();