那曲檬骨新材料有限公司

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

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

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

ARM Cortex-M學(xué)習(xí)筆記:GPIO流水燈的前世今生

CHANBAEK ? 來(lái)源:嵌入式實(shí)驗(yàn)樓 ? 作者:BruceOu ? 2023-05-15 14:44 ? 次閱讀

開(kāi)發(fā)環(huán)境:Keil 5.30

單片機(jī)STM32F103ZET6

上一章通過(guò)控制GPIO的高低電平實(shí)現(xiàn)了流水燈,但只是告訴了大家怎么做,如何實(shí)現(xiàn)流水燈,本文將深入剖析的GPIO流水燈的前生今世,深入研究流水燈的調(diào)用邏輯和數(shù)據(jù)結(jié)構(gòu)。

2.1 GPIO配置概述

前面一章一個(gè)大概講解GPIO的配置過(guò)程和核心的寄存器,當(dāng)然啦,關(guān)于GPIO的寄存器遠(yuǎn)不止我上一章列出來(lái)的,還有很多,具體請(qǐng)參看《STM32F10XXX參考手冊(cè)》中GPIO相關(guān)的內(nèi)容吧。

根據(jù)前面實(shí)現(xiàn)的GPIO流水燈,本文將其歸納如下:

要想控制3個(gè)LED依次亮滅,就需要做以上三件事:使能時(shí)鐘,配置GPIO參數(shù),最后循環(huán)控制GPIO的高低電平就能實(shí)現(xiàn)流水燈的效果,GPIO的寄存器這里就不說(shuō)了,更多詳細(xì)的寄存器描述看官方手冊(cè)就行,下面先來(lái)看看STM32的時(shí)鐘。

2.2 Cortex-M的時(shí)鐘系統(tǒng)

2.2.1 Cortex-M的系統(tǒng)架構(gòu)

基于Cortex-M的系統(tǒng)架構(gòu)比51單片機(jī)強(qiáng)大很多了。首先我們看看Cortex-M3的系統(tǒng)架構(gòu)圖:

對(duì)于對(duì)比,在看看GD32F407xx的系統(tǒng)架構(gòu)圖。

GD32F407xx是基于Cortex-M4設(shè)計(jì)的??梢钥吹?,Cortex-M3和Cortex-M4大體是相同的,系統(tǒng)主要由四個(gè)驅(qū)動(dòng)單元和四個(gè)被動(dòng)單元構(gòu)成。

  • 四個(gè)驅(qū)動(dòng)單元
  • 內(nèi)核 DCode 總線;
  • 系統(tǒng)總線;
  • 通用 DMA1(DMA0);
  • 通用 DMA2(DMA1);
  • 四被動(dòng)單元
  • AHB 到 APB 的橋:連接所有的 APB 設(shè)備;
  • 內(nèi)部 FlASH 閃存;
  • 內(nèi)部 SRAM;
  • FSMC;

下面我們具體講解一下圖中幾個(gè)總線的知識(shí):

ICode 總線:該總線將 M3 內(nèi)核指令總線和閃存指令接口相連,指令的預(yù)取在該總線上面完成。

② DCode 總線:該總線將 M3 內(nèi)核的 DCode 總線與閃存存儲(chǔ)器的數(shù)據(jù)接口相連接,常量加載和調(diào)試訪問(wèn)在該總線上面完成。

③系統(tǒng)總線:該總線連接 M3 內(nèi)核的系統(tǒng)總線到總線矩陣,總線矩陣協(xié)調(diào)內(nèi)核和 DMA 間訪問(wèn)。

④ DMA 總線:該總線將 DMA 的 AHB 主控接口與總線矩陣相連,總線矩陣協(xié)調(diào) CPU 的DCode 和 DMA 到 SRAM,閃存和外設(shè)的訪問(wèn)。

⑤總線矩陣:總線矩陣協(xié)調(diào)內(nèi)核系統(tǒng)總線和 DMA 主控總線之間的訪問(wèn)仲裁,仲裁利用輪換算法。

⑥ AHB/APB 橋:這兩個(gè)橋在 AHB 和 2 個(gè) APB 總線間提供同步連接, APB1 操作速度限于36MHz,APB2 操作速度全速。

對(duì)于系統(tǒng)架構(gòu)的知識(shí),在剛開(kāi)始學(xué)習(xí) STM32 的時(shí)候只需要一個(gè)大概的了解,大致知道是個(gè)什么情況即可。

2.2.2 STM32時(shí)鐘架構(gòu)

時(shí)鐘是整個(gè)處理器運(yùn)行的基礎(chǔ),時(shí)鐘信號(hào)推動(dòng)處理器內(nèi)各個(gè)部分執(zhí)行相應(yīng)的指令。時(shí)鐘系統(tǒng)就是CPU的脈搏,決定CPU速率,像人的心跳一樣只有有了心跳,人才能做其他的事情,而單片機(jī)有了時(shí)鐘,才能夠運(yùn)行執(zhí)行指令,才能夠做其他的處理 (點(diǎn)燈,串口,ADC),時(shí)鐘的重要性不言而喻。

我們?cè)趯W(xué)習(xí)51單片機(jī)時(shí),其最小系統(tǒng)必有晶振電路,這塊電路就是單片機(jī)的時(shí)鐘來(lái)源,晶振的振蕩頻率直接影響單片機(jī)的處理速度。STM32相比51單片機(jī)就復(fù)雜得多,不僅是外設(shè)非常多,就連時(shí)鐘來(lái)源就有四個(gè)。但我們實(shí)際使用的時(shí)候只會(huì)用到有限的幾個(gè)外設(shè),使用任何外設(shè)都需要時(shí)鐘才能啟動(dòng),但并不是所有的外設(shè)都需要系統(tǒng)時(shí)鐘那么高的頻率,為了兼容不同速度的設(shè)備,有些高速,有些低速,如果都用高速時(shí)鐘,勢(shì)必造成浪費(fèi),而且,同一個(gè)電路,時(shí)鐘越快功耗越快,同時(shí)抗電磁干擾能力也就越弱,所以較為復(fù)雜的MCU都是采用多時(shí)鐘源的方法來(lái)解決這些問(wèn)題,因此便有了STM32的時(shí)鐘系統(tǒng)和時(shí)鐘樹(shù)。

STM32三個(gè)不同的時(shí)鐘源可以用來(lái)驅(qū)動(dòng)系統(tǒng)時(shí)鐘(SYSCLK):

● HSI晶振時(shí)鐘(高速內(nèi)部時(shí)鐘信號(hào))

● HSE晶振時(shí)鐘(高速外部時(shí)鐘信號(hào))

● PLL時(shí)鐘

STM32有兩個(gè)二級(jí)時(shí)鐘源:

● 40kHz的低速內(nèi)部RC,它可以驅(qū)動(dòng)獨(dú)立看門狗,還可選擇地通過(guò)程序選擇驅(qū)動(dòng)RTC。 RTC用于從停機(jī)/待機(jī)模式下自動(dòng)喚醒系統(tǒng)。

● 32.768kHz的低速外部晶振,可選擇它用來(lái)驅(qū)動(dòng)RTC(RTCCLK)。

每個(gè)時(shí)鐘源在不使用時(shí)都可以單獨(dú)被打開(kāi)或關(guān)閉,這樣就可以優(yōu)化系統(tǒng)功耗。

當(dāng)使用HSI作為PLL時(shí)鐘的輸入時(shí),所能達(dá)到的最大系統(tǒng)時(shí)鐘為64MHz。

2.2.3 STM32時(shí)鐘硬件電路

2.2.3.1 HSE時(shí)鐘

高速外部時(shí)鐘信號(hào)(HSE)由以下兩種時(shí)鐘源產(chǎn)生:

● HSE外部晶體 / 陶瓷諧振器(見(jiàn)下圖(a))

● HSE用戶外部時(shí)鐘(見(jiàn)下圖(b))

(a)外部時(shí)鐘 (b)晶振時(shí)鐘

1. 外部時(shí)鐘源(HSE旁路)

在這種模式下,必須提供一個(gè)外部時(shí)鐘源。它的頻率可高達(dá)25MHz。外部時(shí)鐘信號(hào)(占空比為50%的方波、正弦波或三角波)必須連到OSC_IN引腳,同時(shí)保證OSC_OUT引腳懸空,見(jiàn)上圖(a)。這個(gè)外部時(shí)鐘源是指從其他處理器等引入的時(shí)鐘源,STM32的demo板就是使用的這種方式,主控器MCU的外部時(shí)鐘源來(lái)自ST Link處理器提供的時(shí)鐘信號(hào)。

2. 外部晶體/陶瓷諧振器(HSE晶體)

這個(gè)4~16MHz的外部晶振的優(yōu)點(diǎn)在于能產(chǎn)生非常精確的主時(shí)鐘。下圖顯示了它需要的相關(guān)硬件配置。諧振器和負(fù)載電容需要盡可能近地靠近振蕩器的引腳,以減小輸出失真和啟動(dòng)穩(wěn)定時(shí)間。負(fù)載電容值必須根據(jù)選定的晶振進(jìn)行調(diào)節(jié)。這種方式也是我們常用的方式,具體電路如下所示。

2.2.3.2 LSE時(shí)鐘

低速外部時(shí)鐘源(LSE)可以由兩個(gè)可能的時(shí)鐘源來(lái)產(chǎn)生:

● LSE外部晶體 / 陶瓷諧振器(見(jiàn)下圖(a))

● LSE用戶外部時(shí)鐘(見(jiàn)下圖(b))

1. 外部源(LSE旁路)

在這種模式下,必須提供一個(gè)外部時(shí)鐘源。它的頻率必須為32.768kHz。外部信號(hào)(占空比為50%的方波、正弦波或三角波)必須連到OSC32_IN引腳,同時(shí)保證OSC_OUT引腳懸空。

2. 外部晶體/陶瓷諧振器(LSE晶體)

這個(gè)LSE晶體是一個(gè)32.768kHz的低速外部晶體或陶瓷諧振器。它的優(yōu)點(diǎn)在于能為實(shí)時(shí)時(shí)鐘部件(RTC)提供一個(gè)低速的,但高精確的時(shí)鐘源。 RTC可以用于時(shí)鐘/日歷或其它需要計(jì)時(shí)的場(chǎng)合。諧振器和加載電容需要盡可能近地靠近晶振引腳,這樣能使輸出失真和啟動(dòng)穩(wěn)定時(shí)間減到最小。負(fù)載電容值必須根據(jù)選定的晶振進(jìn)行調(diào)節(jié)。

(a)外部時(shí)鐘 (b)晶振時(shí)鐘

外部晶體時(shí)鐘如下圖所示。

HSE和LSE外部晶體兩時(shí)鐘電路的兩個(gè)電容式為了抗干擾。對(duì)抗自然界中的一些干擾,如雷擊。

2.2.4 STM32的時(shí)鐘系統(tǒng)

STM32 芯片為了實(shí)現(xiàn)低功耗,設(shè)計(jì)了一個(gè)功能完善但卻非常復(fù)雜的時(shí)鐘系統(tǒng)。普通的MCU 一般只要配置好 GPIO 的寄存器就可以使用了,但 STM32 還有一個(gè)步驟,就是開(kāi)啟外設(shè)時(shí)鐘。

在 STM32 中,可分為五種時(shí)鐘源,為 HSI、 HSE、 LSI、 LSE、 PLL。從時(shí)鐘頻率來(lái)分可以分為高速時(shí)鐘源和低速時(shí)鐘源,其中 HIS, HSE 以及 PLL 是高速時(shí)鐘, LSI 和 LSE 是低速時(shí)鐘。從來(lái)源可分為外部時(shí)鐘源和內(nèi)部時(shí)鐘源,外部時(shí)鐘源就是從外部通過(guò)接晶振的方式獲取時(shí)鐘源,其中 HSE 和 LSE 是外部時(shí)鐘源,其他的是內(nèi)部時(shí)鐘源。下面我們看看 STM32 的 5 個(gè)時(shí)鐘源,我們講解順序是按圖中紅圈標(biāo)示的順序:

1HSI 是 高速內(nèi)部時(shí)鐘 , RC 振蕩器,頻率為 8MHz。

②HSE 是 高速外部時(shí)鐘 ,可接石英 /陶瓷諧振器,或者接外部時(shí)鐘源,頻率范圍為4MHz~16MHz。 我們的開(kāi)發(fā)板接的是 8M 的晶振。 當(dāng)使用有源晶振時(shí),時(shí)鐘從 OSC_IN 引腳進(jìn)入, OSC_OUT 引腳懸空,當(dāng)選用無(wú)源晶振時(shí),時(shí)鐘從 OSC_IN 和 OSC_OUT 進(jìn)入,并且要配諧振電容。 HSE 最常使用的就是 8M 的無(wú)源晶振。 當(dāng)確定 PLL 時(shí)鐘來(lái)源的時(shí)候, HSE 可以不分頻或者 2 分頻,這個(gè)由時(shí)鐘配置寄存器 CFGR 的位 17。

③LSI 是 低速內(nèi)部時(shí)鐘 ,RC 振蕩器,頻率為 40kHz。 獨(dú)立看門狗的時(shí)鐘源只能是 LSI,同時(shí) LSI 還可以作為 RTC 的時(shí)鐘源。

④LSE 是 低速外部時(shí)鐘 ,接頻率為 32.768kHz 的石英晶體。 這個(gè)主要是 RTC 的時(shí)鐘源。

⑤PLL 為鎖相環(huán)倍頻輸出,其時(shí)鐘輸入源可選擇為 HSI/2、HSE 或者 HSE/2。 倍頻可選擇為2~16 倍,但是其輸出頻率最大不得超過(guò) 72MHz。

圖中我們用 A~E 標(biāo)示我們要講解的地方。

A. MCO 是 STM32 的一個(gè)時(shí)鐘輸出 IO(PA8),它可以選擇一個(gè)時(shí)鐘信號(hào)輸出,可以選擇為 PLL 輸出的 2 分頻、 HSI、 HSE、或者系統(tǒng)時(shí)鐘。 這個(gè)時(shí)鐘可以用來(lái)給外部其他系統(tǒng)提供時(shí)鐘源。

B. 這里是 RTC 時(shí)鐘源,從圖上可以看出, RTC 的時(shí)鐘源可以選擇 LSI, LSE,以及HSE 的 128 分頻。

C. 從圖中可以看出 C 處 USB 的時(shí)鐘是來(lái)自 PLL 時(shí)鐘源。 STM32 中有一個(gè)全速功能的 USB 模塊,其串行接口引擎需要一個(gè)頻率為 48MHz 的時(shí)鐘源。該時(shí)鐘源只能從 PLL 輸出端獲取,可以選擇為 1.5 分頻或者 1 分頻,也就是,當(dāng)需要使用 USB模塊時(shí), PLL 必須使能,并且時(shí)鐘頻率配置為 48MHz 或 72MHz。

D. D 處就是 STM32 的系統(tǒng)時(shí)鐘 SYSCLK,它是供 STM32 中絕大部分部件工作的時(shí)鐘源。系統(tǒng)時(shí)鐘可選擇為 PLL 輸出、 HSI 或者 HSE。系統(tǒng)時(shí)鐘最大頻率為 72MHz,當(dāng)然你也可以超頻,不過(guò)一般情況為了系統(tǒng)穩(wěn)定性是沒(méi)有必要冒風(fēng)險(xiǎn)去超頻的。

E. 這里的 E 處是指其他所有外設(shè)了。從時(shí)鐘圖上可以看出,其他所有外設(shè)的時(shí)鐘最終來(lái)源都是 SYSCLK。 SYSCLK 通過(guò) AHB 分頻器分頻后送給各模塊使用。這些模塊包括:

①AHB 總線、內(nèi)核、內(nèi)存和 DMA 使用的 HCLK 時(shí)鐘。

②通過(guò) 8 分頻后送給 Cortex 的系統(tǒng)定時(shí)器時(shí)鐘,也就是 systick 了。

③直接送給 Cortex 的空閑運(yùn)行時(shí)鐘 FCLK。

④送給 APB1 分頻器。 APB1 分頻器輸出一路供 APB1 外設(shè)使用(PCLK1,最大頻率 36MHz),另一路送給定時(shí)器(Timer)2、 3、 4 倍頻器使用。

⑤送給 APB2 分頻器。 APB2 分頻器分頻輸出一路供 APB2 外設(shè)使用(PCLK2,最大頻率 72MHz),另一路送給定時(shí)器(Timer)1 倍頻器使用。

其中需要理解的是 APB1 和 APB2 的區(qū)別, APB1 上面連接的是低速外設(shè),包括電源接口、備份接口、 CAN、 USB、 I2C1、 I2C2、 UART2、 UART3 等等, APB2 上面連接的是高速外設(shè)包括 UART1、 SPI1、 Timer1、 ADC1、 ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等。

不同的總線有不同的頻率,不同的外設(shè)掛在不同的總線下,為了更適合初學(xué)者查閱,筆者把常用的外設(shè)與總線的對(duì)應(yīng)關(guān)系總結(jié)如下:

SystemInit()函數(shù)中設(shè)置的系統(tǒng)時(shí)鐘大小:

  • SYSCLK(系統(tǒng)時(shí)鐘) =72MHz
  • AHB 總線時(shí)鐘(使用 SYSCLK) =72MHz
  • APB1 總線時(shí)鐘(PCLK1) =36MHz
  • APB2 總線時(shí)鐘(PCLK2) =72MHz
  • PLL 時(shí)鐘 =72MHz

具體代碼請(qǐng)讀者查看工程文件的system_stm32f10x.c文件。

舉個(gè)例子:Keil編寫程序是默認(rèn)的時(shí)鐘為72Mhz,其實(shí)是這么來(lái)的:外部晶振(HSE)提供的8MHz(與電路板上的晶振的相關(guān))通過(guò)PLLXTPRE分頻器后,進(jìn)入PLLSRC選擇開(kāi)關(guān),進(jìn)而通過(guò)PLLMUL鎖相環(huán)進(jìn)行倍頻(x9)后,為系統(tǒng)提供72MHz的系統(tǒng)時(shí)鐘(SYSCLK)。 之后是AHB預(yù)分頻器對(duì)時(shí)鐘信號(hào)進(jìn)行分頻,然后為低速外設(shè)提供時(shí)鐘。

或者內(nèi)部RC振蕩器(HSI) 為8MHz /2 為4MHz 進(jìn)入PLLSRC選擇開(kāi)關(guān),通過(guò)PLLMUL鎖相環(huán)進(jìn)行倍頻(x18)后為72MHz。

PS: 網(wǎng)上有很多人說(shuō)是5個(gè)時(shí)鐘源,這種說(shuō)法有點(diǎn)問(wèn)題,學(xué)習(xí)之后就會(huì)發(fā)現(xiàn)PLL并不是自己產(chǎn)生的時(shí)鐘源,而是通過(guò)其他三個(gè)時(shí)鐘源倍頻得到的時(shí)鐘,這點(diǎn)在前文已近講解得很清楚了。

2.2.5 STM32的時(shí)鐘配置剖析

既然時(shí)鐘搞清楚了,接下來(lái)回到上一章的配置時(shí)鐘的代碼:

/*開(kāi)啟LED的外設(shè)時(shí)鐘*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE);

RCC_APB2PeriphClockCmd就是配置時(shí)鐘的函數(shù),函數(shù)原型如下:

/**
 * @brief  Enables or disables theHigh Speed APB (APB2) peripheral clock.
 * @param  RCC_APB2Periph:specifies the APB2 peripheral to gates its clock.
 *   This parameter can be anycombination of the following values:
 *     @arg RCC_APB2Periph_AFIO,RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
 *          RCC_APB2Periph_GPIOC,RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
 *          RCC_APB2Periph_GPIOF,RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
 *          RCC_APB2Periph_ADC2,RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
 *          RCC_APB2Periph_TIM8,RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
 *          RCC_APB2Periph_TIM15,RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
 *          RCC_APB2Periph_TIM9,RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11    
 * @param  NewState: new state ofthe specified peripheral clock.
 *   This parameter can be: ENABLEor DISABLE.
 * @retval None
 */
voidRCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
 /* Check the parameters */
 assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
 assert_param(IS_FUNCTIONAL_STATE(NewState));
 if (NewState != DISABLE)
 {
    RCC->APB2ENR |= RCC_APB2Periph;
 }
 else
 {
    RCC->APB2ENR &= ~RCC_APB2Periph;
 }
}

第一個(gè)參數(shù)就是具體的外設(shè)時(shí)鐘,第二參數(shù)就是時(shí)鐘狀態(tài)開(kāi)關(guān),整個(gè)函數(shù)很簡(jiǎn)單,就是配置RCC->APB2ENR,assert_param是檢查參數(shù)的,關(guān)于assert_param詳細(xì)講解看附錄的小貼士。

參數(shù)RCC_APB2Periph傳入值是通過(guò)宏來(lái)定義的,這樣的好處也是便于移植,如果換了MCU,架構(gòu)一樣,只需要就該底層驅(qū)動(dòng)就行,不需要更改上層應(yīng)用,這樣就提高了開(kāi)發(fā)效率。 言歸正傳,我們傳入的RCC_APB2Periph_GPIOB和RCC_APB2Periph_GPIOG定義如下。

其實(shí)就是一個(gè)個(gè)偏移而已。 我們還是看看RCC_APB2ENR寄存器,

如果要配置GPIOB的時(shí)鐘就是要將第3位置1,因此轉(zhuǎn)換成10進(jìn)制就是8,同理GPIOG也是一樣的。

RCC_APB2ENR的地址是0x18,更準(zhǔn)確的是說(shuō)偏移地址是0x18,在代碼中也是有體現(xiàn)的,我們看看RCC結(jié)構(gòu)體吧。

#define RCC                 ((RCC_TypeDef *) RCC_BASE)

這里是通過(guò)宏的方式定義的,結(jié)構(gòu)體就是RCC_TypeDef。

/**
 * @brief Reset and Clock Control
 */

typedef struct
{
 __IO uint32_t CR;
 __IO uint32_t CFGR;
 __IO uint32_t CIR;
 __IO uint32_t APB2RSTR;
 __IO uint32_t APB1RSTR;
 __IO uint32_t AHBENR;
 __IO uint32_t APB2ENR;
 __IO uint32_t APB1ENR;
 __IO uint32_t BDCR;
 __IO uint32_t CSR;

#ifdef STM32F10X_CL 
 __IO uint32_t AHBRSTR;
 __IO uint32_t CFGR2;
#endif /* STM32F10X_CL */

#if defined (STM32F10X_LD_VL) ||defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)  
 uint32_t RESERVED0;
 __IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL ||STM32F10X_MD_VL || STM32F10X_HD_VL */
} RCC_TypeDef;

這里可以看到有__IO修飾結(jié)構(gòu)體成員,關(guān)于__IO詳細(xì)講解看附錄的小貼士。 RCC_TypeDef中的成員都是32位,因此偏移都是4,而APB2ENR的偏移量就是寄存器表的偏移一一對(duì)應(yīng)了,其他的結(jié)構(gòu)體都是這樣定義的。

在說(shuō)說(shuō)RCC_BASE,這就實(shí)際的RCC的物理地址,其中的依賴關(guān)系如下:

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!

這樣我們就知道最終的RCC的基地址是0x0x40021000。 這個(gè)地址也可以在參考手冊(cè)中找到。

是不就都對(duì)上了,MCU所有的外設(shè)都對(duì)應(yīng)了一個(gè)地址,因此通過(guò)操作該地址就能控制相應(yīng)的功能,關(guān)于STM32的存儲(chǔ)管理在后文會(huì)詳細(xì)描述,這里先了解個(gè)大概就好。

值得注意的是,既然有RCC_APB2PeriphClockCmd函數(shù),肯定還有RCC_APB1PeriphClockCmd,它們各自配置的時(shí)鐘可以從系統(tǒng)架構(gòu)中看到,當(dāng)然啦,函數(shù)的說(shuō)明中也列舉了具體的外設(shè)。

到這里基本上時(shí)鐘的配置就完成了。 但是我想還是有很多朋友沒(méi)有很明白,下面就GPIO的配置從地址映射和固件庫(kù)的封裝兩個(gè)方便再來(lái)詳細(xì)總結(jié)下固件庫(kù)是如何完成GPIO的具體操作的。

2.3 Cortex-M的地址映射

我們先看看51 單片機(jī)中是怎么做的,51 單片機(jī)開(kāi)發(fā)中會(huì)引用一個(gè) reg51.h 的頭文件,51單片機(jī)是通過(guò)以下方式將名字和寄存器聯(lián)系起來(lái)的:

sfr P0 =0x80;

sfr 也是一種擴(kuò)充數(shù)據(jù)類型,占用一個(gè)內(nèi)存單元,值域?yàn)?0~255。 利用它可以訪問(wèn) 51 單片機(jī)內(nèi)部的所有特殊功能寄存器。 如用 sfr P1 = 0x90 這一句定義 P1 為 P1 端口在片內(nèi)的寄存器。 然后我們往地址為 0x80 的寄存器設(shè)值的方法是: P0=value; 通過(guò)改變value的值來(lái)控制單片機(jī)。

所謂地址映射,就是將芯片上的存儲(chǔ)器甚至 I/O 等資源與地址建立一一對(duì)應(yīng)的關(guān)系。 如果某地址對(duì)應(yīng)著某寄存器,我們就可以運(yùn)用 C 語(yǔ)言的指針來(lái)尋址并修改這個(gè)地址上的內(nèi)容,從而實(shí)現(xiàn)修改該寄存器的內(nèi)容。 Cortex-M的地址映射也是類似的。 Cortex-M有 32 根地址線,所以它的尋址空間大小為 2 ^32^bit=4 GB。 ARM 公司設(shè)計(jì)時(shí),預(yù)先把這 4 GB 的尋址空間大致地分配好了。 它把從 0x40000000 至 0x5FFFFFFF( 512 MB)的地址分配給片上外設(shè)。 通過(guò)把片上外設(shè)的寄存器映射到這個(gè)地址區(qū),就可以簡(jiǎn)單地以訪問(wèn)內(nèi)存的方式,訪問(wèn)這些外設(shè)的寄存器,從而控制外設(shè)的工作。 這樣,片上外設(shè)可以使用 C 語(yǔ)言來(lái)操作。

stm32f10x.h 這個(gè)文件中重要的內(nèi)容就是把 STM32 的所有寄存器進(jìn)行地址映射。 如同51 單片機(jī)的

在這里我們以流水燈中的 GPIOB 為例進(jìn)行剖析,如果是其他的 IO 端口,則改成相應(yīng)的地址即可。 在這個(gè)文件中一系列宏實(shí)現(xiàn)了地址映射。

#define GPIOB_BASE(APB2PERIPH_BASE + 0xC00)
#define APB2PERIPH_BASE (PERIPH_BASE+ 0xC00
#define PERIPH_BASE((uint32_t)0x40000000)

這幾個(gè)宏定義是從文件中的幾個(gè)部分抽離出來(lái)的,具體的內(nèi)容讀者可參考stm32f10x.h 源碼。

首先看到 PERIPH_BASE 這個(gè)宏,宏展開(kāi)為 0x40000000,并把它強(qiáng)制轉(zhuǎn)換為 uint32_t的 32 位類型數(shù)據(jù),這是因?yàn)?STM32 的地址是 32 位的,0x40000000 這個(gè)地址是 Cortex-M3 核分配給片上外設(shè) 512MB 尋址空間中的第一個(gè)地址,我們把0x40000000 稱為外設(shè)基地址。

接下來(lái)是宏 APB2PERIPH_BASE,宏展開(kāi)為 PERIPH_BASE(外設(shè)基地址)加上偏移地址 0x10000,即指向的地址為 0x40010000。 這個(gè) APB2PERIPH_BASE 宏是什么地址呢? STM32 不同的外設(shè)是掛載在不同的總線上的。 STM32 芯片有 AHB 總線、APB2總線和 APB1 總線,掛載在這些總線上的外設(shè)有特定的地址范圍。

其中像 GPIO、串口 1、ADC 及部分定時(shí)器是掛載在稱為 APB2 的總線上,掛載到APB2 總線上的外設(shè)地址空間是從0x40010000 至 0x40013FFF地址。 這里的第一個(gè)地址,也就是 0x40010000,稱為 APB2PERIPH_BASE (APB2 總線外設(shè)基地址)。

而 APB2 總線基地址相對(duì)于外設(shè)基地址的偏移量為 0x10000 個(gè)地址,即為 APB2 相對(duì)外設(shè)基地址的偏移地址,見(jiàn)下表。

由這個(gè)表我們可以知道,stm32f10x.h 這個(gè)文件中必然含有用于定義總線外設(shè)基地址的宏。

#define APB2PERIPH_BASE  PERIPH_BASE

因?yàn)槠屏繛榱?,所?APB2的地址直接就等于外設(shè)基地址。

最后到了宏 GPIOB_BASE,宏展開(kāi)為 APB2PERIPH_BASE (APB2 總線外設(shè)的基地址)加上相對(duì) APB2 總線外設(shè)基地址的偏移量 0xC00 得到了 GPIOB端口的寄存器組的基地址。 這個(gè)所謂的寄存器組又是什么呢? 它包括什么寄存器?

細(xì)看 stm32f10x.h 文件,我們還可以發(fā)現(xiàn)有關(guān)各個(gè) GPIO 基地址的宏。

#define GPIOA_BASE(APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE(APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE(APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE(APB2PERIPH_BASE + 0x1400)

除了 GPIOB寄存器組的地址,還有 GPIOA、GPIOC和 GPIOD 的地址,并且這些地址是不一樣的。 前面提到,每組 GPIO 都對(duì)應(yīng)著獨(dú)立的一組寄存器,查看 STM32 的數(shù)據(jù)手冊(cè)。

注意到這個(gè)說(shuō)明中有一個(gè)偏移地址:0x04,這里的偏移地址是相對(duì)哪個(gè)地址的偏移呢? 下面進(jìn)行舉例說(shuō)明。

對(duì)于GPIOB組的寄存器,GPIOB含有的端口配置高寄存器(GPIOB_CRH地址為:GPIOB_BASE +0x04。 假如是 GPIOA 組的寄存器,則 GPIOA 含有的端口配置高寄存器(GPIOA_CRH)地址為:GPIOA_BASE+0x04。 也就是說(shuō),這個(gè)偏移地址,就是該寄存器相對(duì)所在寄存器組基地址的偏移量。

2.4固件庫(kù)對(duì)寄存器的封裝

ST的工程師用結(jié)構(gòu)體的形式封裝了寄存器組,在 stm32f10x.h 文件定義的。

#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)

有了這些宏,我們就可以定位到具體的寄存器地址,結(jié)構(gòu)體GPIO_TypeDef在 stm32f10x.h 文件中定義的。

typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;

結(jié)構(gòu)體內(nèi)又定義了 7 個(gè) __IO uint32_t 類型的變量。 這些變量都是 32 位,即每個(gè)變量占內(nèi)存空間 4 個(gè)字節(jié)。 在 C 語(yǔ)言中,結(jié)構(gòu)體內(nèi)變量的存儲(chǔ)空間是連續(xù)的,也就是說(shuō)假如我們定義了一個(gè) GPIO_TypeDef ,這個(gè)結(jié)構(gòu)體的首地址(變量 CRL 的地址)若為 0x40011000,那么結(jié)構(gòu)體中第二個(gè)變量(CRH)的地址即為 0x40011000 +0x04, 加上的0x04 正是代表 4 個(gè)字節(jié)地址的偏移量。

0x04 偏移量正是 GPIOx_CRH 寄存器相對(duì)于所在寄存器組的偏移地址。 同理,GPIO_TypeDef 結(jié)構(gòu)體內(nèi)其他變量的偏移量,也與相應(yīng)的寄存器偏移地址相符。 于是,只要我們匹配了結(jié)構(gòu)體的首地址,就可以確定各寄存器的具體地址了。

GPIOA_BASE 在前文已解析,是一個(gè)代表 GPIOA組寄存器的基地址。“(GPIO_TypeDef *)”在這里的作用則是把 GPIOA_BASE 地址轉(zhuǎn)換為 GPIO_TypeDef結(jié)構(gòu)體指針類型。有了這樣的宏,以后我們寫代碼的時(shí)候,如果要修改GPIO 的寄存器,就可用修改以下代碼的方式來(lái)實(shí)現(xiàn)。

GPIO_TypeDef * GPIOx; //定義一個(gè) GPIO_TypeDef 型結(jié)構(gòu)體指針 GPIOx
GPIOx = GPIOA; //把指針地址設(shè)置為宏 GPIOA 地址
GPIOx->CRL = 0xffffffff; //通過(guò)指針訪問(wèn)并修改 GPIOA_CRL 寄存器

通過(guò)類似的方式,我們就可以給具體的寄存器寫上適當(dāng)?shù)膮?shù)以控制 STM32 了。這樣我們就可以通過(guò)庫(kù)函數(shù)實(shí)現(xiàn)了GPIO的初始化了。

/**
 * @brief  初始化LED的GPIO
 * @param  None
 * @retval None
 */
void LED_GPIO_Config(void)
{            
        /*定義一個(gè)GPIO_InitTypeDef類型的結(jié)構(gòu)體*/
        GPIO_InitTypeDefGPIO_InitStructure;


        /*開(kāi)啟LED的外設(shè)時(shí)鐘*/
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE);


        /*設(shè)置IO口*/                                                                    
        GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //設(shè)置引腳模式為通用推挽輸出
        GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz; //設(shè)置引腳速率為50MHz


        /*調(diào)用庫(kù)函數(shù),初始化GPIOB0*/
        GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;      //選擇要控制的GPIOB引腳       
        GPIO_Init(GPIOB,&GPIO_InitStructure);    

        GPIO_InitStructure.GPIO_Pin= GPIO_Pin_6|GPIO_Pin_7;/*選擇要控制的引腳*/         
        GPIO_Init(GPIOG,&GPIO_InitStructure);   

        /*開(kāi)啟所有l(wèi)ed燈  */
        GPIO_SetBits(GPIOB,GPIO_Pin_0);
        GPIO_SetBits(GPIOG,GPIO_Pin_6|GPIO_Pin_7);       
}

當(dāng)然啦,上述代碼包含了時(shí)鐘的使能。

通過(guò)對(duì)時(shí)鐘和GPIO的分析,我想大家已經(jīng)對(duì)固件的邏輯有了一定的認(rèn)識(shí),從本質(zhì)上講,都是在配置寄存器,只是地址和值不同罷了,而固件庫(kù)就是對(duì)寄存器配置的封裝,便于開(kāi)發(fā)者調(diào)用。

當(dāng)然啦,本文是基于標(biāo)準(zhǔn)庫(kù)分析,HAL庫(kù)的邏輯也是一樣的,只是HAL功能更完善,封裝更徹底,后面也會(huì)詳細(xì)分析HAL庫(kù)的調(diào)用邏輯。

小貼士

1.assert_param

在STM32的固件庫(kù)和提供的例程中,到處都可以見(jiàn)到assert_param()的使用。如果打開(kāi)任何一個(gè)例程中的stm32f10x_conf.h文件,就可以看到實(shí)際上assert_param是一個(gè)宏定義;在固件庫(kù)中,它的作用就是檢測(cè)傳遞給函數(shù)的參數(shù)是否是有效的參數(shù)。

所謂有效的參數(shù)是指滿足規(guī)定范圍的參數(shù),比如某個(gè)參數(shù)的取值范圍只能是小于3的正整數(shù),如果給出的參數(shù)大于3,則這個(gè)assert_param()可以在運(yùn)行的程序調(diào)用到這個(gè)函數(shù)時(shí)報(bào)告錯(cuò)誤,使程序員可以及時(shí)發(fā)現(xiàn)錯(cuò)誤,而不必等到程序運(yùn)行結(jié)果的錯(cuò)誤而大費(fèi)周折。這是一種常見(jiàn)的軟件技術(shù),可以在調(diào)試階段幫助程序員快速地排除那些明顯的錯(cuò)誤。它確實(shí)在程序的運(yùn)行上犧牲了效率(但只是在調(diào)試階段),但在項(xiàng)目的開(kāi)發(fā)上卻幫助你提高了效率。

當(dāng)你的項(xiàng)目開(kāi)發(fā)成功,使用release模式編譯之后,或在stm32f10x_conf.h文件中注釋掉對(duì)USE_FULL_ASSERT的宏定義,所有的assert_param()檢驗(yàn)都消失了,不會(huì)影響最終程序的運(yùn)行效率。在執(zhí)行assert_param()的檢驗(yàn)時(shí),如果發(fā)現(xiàn)參數(shù)出錯(cuò),它會(huì)調(diào)用函數(shù)assert_failed()向程序員報(bào)告錯(cuò)誤,在任何一個(gè)例程中的main.c中都有這個(gè)函數(shù)的模板,如下:

void assert_failed(uint8_t*file, uint32_t line)
{

while (1)
{}
}

你可以按照自己使用的環(huán)境需求,添加適當(dāng)?shù)恼Z(yǔ)句輸出錯(cuò)誤的信息提示,或修改這個(gè)函數(shù)做出適當(dāng)?shù)腻e(cuò)誤處理。

1、STM32F10xD.LIB是DEBUG模式的庫(kù)文件。

2、STM32F10xR.LIB是Release模式的庫(kù)文件。

3、要選擇DEBUG和RELEASE模式,需要修改stm32f10x_conf.h的內(nèi)容。#define DEBUG 表示DEBUG模式,把該語(yǔ)句注釋掉,則為RELEASE模式。

4、要選擇DEBUG和RELEASE模式,也可以在Options,C/C++,Define里填入DEBUG的預(yù)定義。這樣,就不需要修改stm32f10x_conf.h的內(nèi)容。

5、如果把庫(kù)加入項(xiàng)目,則不需要將ST的庫(kù)源文件加入項(xiàng)目,比較方便。但是,庫(kù)的選擇要和DEBUG預(yù)定義對(duì)應(yīng)。

2.__I、 __O 、__IO的含義

這是ST庫(kù)里面的宏定義,定義如下:

#define     __I     volatile const             /*!< defines 'read only' permissions      */
#define     __O    volatile                  /*!< defines 'write only'permissions     */
#define     __IO   volatile                  /*!

顯然,這三個(gè)宏定義都是用來(lái)替換成 volatile 和 const 的,所以我們先要了解這兩個(gè)關(guān)鍵字的作用:

__I :輸入口。既然是輸入,那么寄存器的值就隨時(shí)會(huì)外部修改,那就不能進(jìn)行優(yōu)化,每次都要重新從寄存器中讀取。也不能寫,即只讀,不然就不是輸入而是輸出了。

__O :輸出口,也不能進(jìn)行優(yōu)化,不然你連續(xù)兩次輸出相同值,編譯器認(rèn)為沒(méi)改變,就忽略了后面那一次輸出,假如外部在兩次輸出中間修改了值,那就影響輸出。

__IO:輸入輸出口,同上

為什么加下劃線?

原因是:避免命名沖突

一般宏定義都是大寫,但因?yàn)檫@里的字母比較少,所以再添加下劃線來(lái)區(qū)分。這樣一般都可以避免命名沖突問(wèn)題,因?yàn)楹苌偃诉@樣命名,這樣命名的人肯定知道這些是有什么用的。

經(jīng)常寫大工程時(shí),都會(huì)發(fā)現(xiàn)老是命名沖突,要不是全局變量沖突,要不就是宏定義沖突,所以我們要盡量避免這些問(wèn)題,不然出問(wèn)題了都不知道問(wèn)題在哪里。

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

    關(guān)注

    146

    文章

    17317

    瀏覽量

    352635
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    134

    文章

    9165

    瀏覽量

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

    關(guān)注

    31

    文章

    5363

    瀏覽量

    121157
  • 流水燈
    +關(guān)注

    關(guān)注

    21

    文章

    433

    瀏覽量

    59832
  • GPIO
    +關(guān)注

    關(guān)注

    16

    文章

    1216

    瀏覽量

    52377
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    GD32開(kāi)發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)篇) 第3章 GPIO流水燈前世今生

    上一章通過(guò)控制GPIO的高低電平實(shí)現(xiàn)了流水燈,但只是告訴了大家怎么做,如何實(shí)現(xiàn)流水燈,本文將深入剖析的GPIO流水燈的前生今世,深入研究
    的頭像 發(fā)表于 05-10 08:58 ?5407次閱讀
    GD32開(kāi)發(fā)實(shí)戰(zhàn)指南(基礎(chǔ)篇) 第3章 <b class='flag-5'>GPIO</b><b class='flag-5'>流水燈</b>的<b class='flag-5'>前世</b><b class='flag-5'>今生</b>

    ARM Cortex-M學(xué)習(xí)筆記:初識(shí)GPIO流水燈

    熟悉單片機(jī)的朋友都知道,學(xué)習(xí)的第一個(gè)例程就是流水燈,要想實(shí)現(xiàn)流水燈,首先必須了解GPIO的工作原理。GPIO的基本結(jié)構(gòu)如圖1所示。
    的頭像 發(fā)表于 05-15 11:31 ?3095次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>Cortex-M</b><b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>:初識(shí)<b class='flag-5'>GPIO</b><b class='flag-5'>流水燈</b>

    ARM Cortex-M學(xué)習(xí)筆記:初識(shí)Systick定時(shí)器

    Cortex-M的內(nèi)核中包含Systick定時(shí)器了,只要是Cortex-M系列的MCU就會(huì)有Systick,因此這是通用的,下面詳細(xì)分析。
    的頭像 發(fā)表于 05-15 15:01 ?3217次閱讀
    <b class='flag-5'>ARM</b> <b class='flag-5'>Cortex-M</b><b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>:初識(shí)Systick定時(shí)器

    芯片春秋——ARM前世今生

    芯片春秋 ARM前世今生
    發(fā)表于 05-25 15:05

    ARM Cortex-M堆棧機(jī)制介紹

      大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是ARM Cortex-M堆棧機(jī)制?! 〗裉旖o大家分享的這篇依舊是2016年之前痞子衡寫的技術(shù)文檔,花了點(diǎn)時(shí)間重新編排了一下
    發(fā)表于 12-16 06:26

    ARM Cortex-M內(nèi)核的相關(guān)資料推薦

      大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是ARM Cortex-M功能模塊,不過(guò)側(cè)重點(diǎn)是三款安全特性處理器。  ARM Cortex-M處理器家族發(fā)展至今(2
    發(fā)表于 12-27 07:21

    ARM Cortex-M開(kāi)發(fā)之初識(shí)GPIO流水燈

    1、GPIO工作原理熟悉單片機(jī)的朋友都知道,學(xué)習(xí)的第一個(gè)例程就是流水燈,要想實(shí)現(xiàn)流水燈,首先必須了解GPIO的工作原理。
    發(fā)表于 04-24 17:22

    Arm Cortex-M處理器—Cortex-M85介紹

    Arm發(fā)布了新一代的Cortex-M處理器,Cortex-M85。簡(jiǎn)單粗暴的打個(gè)比方:Cortex-M85 ≈ Cortex-M7Trust
    發(fā)表于 07-15 14:59

    ARM Cortex-M 系列微控制器(ST)

    ARM Cortex-M 系列微控制器(ST) 意法半導(dǎo)體(ST)宣布在基于ARM Cortex-M系列處理器內(nèi)核的微控制器研發(fā)項(xiàng)目上取得突破,推出全球業(yè)內(nèi)首款采用90nm技術(shù)嵌入式
    發(fā)表于 11-02 09:29 ?963次閱讀

    GPIO-流水燈的前后今生

    主要是STM32系列的GPIO-流水燈的前后今生進(jìn)行詳細(xì)的講解,需要的自行下載
    發(fā)表于 06-17 16:48 ?8次下載

    Atmel Studio 6軟件中如何調(diào)試ARM Cortex-M

    Atmel Studio 6軟件中如何調(diào)試ARM Cortex-M
    的頭像 發(fā)表于 07-04 10:49 ?4279次閱讀

    米爾科技Cortex-M Prototyping System +介紹

    ARM? Cortex?-M原型系統(tǒng) MPS2+,為Cortex-M 系列微處理器設(shè)計(jì)的原型驗(yàn)證評(píng)估系統(tǒng),包含最新的Cortex-M7 及
    的頭像 發(fā)表于 11-14 10:45 ?1983次閱讀
    米爾科技<b class='flag-5'>Cortex-M</b> Prototyping System +介紹

    Cortex-MCortex-A認(rèn)識(shí)ARM處理器

    Cortex-MCortex-A認(rèn)識(shí)ARM處理器
    的頭像 發(fā)表于 03-08 11:34 ?3514次閱讀

    [STM32CubeMX]學(xué)習(xí)筆記1:GPIO輸出LED流水燈

    [STM32CubeMX]學(xué)習(xí)筆記1:GPIO輸出LED流水燈
    發(fā)表于 12-05 13:36 ?12次下載
    [STM32CubeMX]<b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>1:<b class='flag-5'>GPIO</b>輸出LED<b class='flag-5'>流水燈</b>

    敏矽微電子Cortex-M0學(xué)習(xí)筆記04——GPIO詳解及應(yīng)用實(shí)例

    敏矽微電子Cortex-M0學(xué)習(xí)筆記04——GPIO詳解及應(yīng)用實(shí)例
    的頭像 發(fā)表于 09-26 17:07 ?1143次閱讀
    敏矽微電子<b class='flag-5'>Cortex-M</b>0<b class='flag-5'>學(xué)習(xí)</b><b class='flag-5'>筆記</b>04——<b class='flag-5'>GPIO</b>詳解及應(yīng)用實(shí)例
    大世界百家乐官网娱乐场| 怎么玩百家乐官网网上赌博| 二八杠规则| 青鹏棋牌官网| 百家乐官网视频台球游戏| 百家乐官网赌场娱乐网规则| 澳门百家乐国际| 大发888 894| 澳门百家乐官网玩法心得技巧| 百家乐怎么样玩| 大发888有赢钱的吗| 百家乐官网最保险的方法| 太子百家乐官网娱乐城| 玩百家乐游戏的最高技巧| 六合彩网址大全| 真钱百家乐官网开户试玩| 百家乐官网博彩免费体验金3| 百家乐筹码套装包邮| 爱博彩论坛| 百家乐官网街机| 缅甸百家乐娱乐场开户注册| 沙龙国际网站| 戒掉百家乐官网的玩法技巧和规则 | 百家乐庄闲局部失衡| 大发888官方 截图| 百家乐官网六手变化混合赢家打法 | 大杀器百家乐学院| 顶尖百家乐官网对单| 百家乐的庄闲概率| 大发888加速器| 累积式百家乐官网的玩法技巧和规则| 新葡京百家乐的玩法技巧和规则| 鞍山市| 游戏机百家乐作弊| 大发888官方| 电脑百家乐官网玩| 百家乐统计工具| 百家乐官网真人娱乐场开户注册 | 百家乐官网代理在线游戏可信吗网上哪家平台信誉好安全 | 百家乐官网筹码皇冠| 菲律宾卡卡湾|