stm32官方例程在哪找_正点原子Linux第十一章模仿STM32驱动开发格式实验
1)資料下載:點擊資料即可下載
2)對正點原子Linux感興趣的同學可以加群討論:935446741
3)關注正點原子公眾號,獲取最新資料更新
第十一章模仿STM32驅動開發格式實驗
在上一章使用C語言編寫LED燈驅動的時候,每個寄存器的地址我們都需要寫宏定義,使用起來非常的不方便。我們在學習STM32的時候,可以使用“GPIOB->ODR”這種方式來給GPIOB的寄存器ODR賦值,因為在STM32中同屬于一個外設的所有寄存器地址基本是相鄰的(有些會有保留寄存器)。因此我們可以借助C語言里面的結構體成員地址遞增的特點來將某個外設的所有寄存器寫入到一個結構體里面,然后定義一個結構體指針指向這個外設的寄存器基地址,這樣我們就可以通過這個結構體指針來訪問這個外設的所有寄存器。同理,I.MX6U也可以使用這種方法來定義外設寄存器,本章我們就模仿STM32里面的寄存器定義方式來編寫I.MX6U的驅動,通過本章的學習也可以對STM32的寄存器定義方式有一個深入的認識。
11.1模仿STM32寄存器定義
11.1.1 STM32寄存器定義簡介
為了開發方便,ST官方為STM32F103編寫了一個叫做stm32f10x.h的文件,在這個文件里面定義了STM32F103所有外設寄存器,我們可以使用其定義的寄存器來進行開發,比如我們可以用如下代碼來初始化一個GPIO:
GPIOE->CRL&=0XFF0FFFFF;
GPIOE->CRL|=0X00300000; //PE5推挽輸出
GPIOE->ODR|=1<<5; //PE5輸出高
上述代碼是初始化STM32的PE5這個GPIO為推挽輸出,需要配置的就是GPIOE的寄存器CRL和ODR,“GPIOE”的定義:
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
可以看出“GPIOE”是個宏定義,是一個指向地址GPIOE_BASE的結構體指針,結構體為GPIO_TypeDef,GPIO_TypeDef和GPIOE_BASE的定義如下:
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;
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
上述定義中GPIO_TypeDef是個結構體,結構體里面的成員變量有CRL、CRH、IDR、ODR、BSRR、BRR和LCKR,這些都是GPIO的寄存器,每個成員變量都是32位(4字節),這些寄存器在結構體中的位置都是按照其地址值從小到大排序的。GPIOE_BASE就是GPIOE的基地址,其為:
GPIOE_BASE=APB2PERIPH_BASE+0x1800
= PERIPH_BASE + 0x10000 + 0x1800
=0x40000000 + 0x10000 + 0x1800
=0x40011800
GPIOE_BASE的基地址為0x40011800,宏GPIOE指向這個地址,因此GPIOE的寄存器CRL的地址就是0X40011800,寄存器CRH的地址就是0X40011800+4=0X40011804,其他寄存器地址以此類推。我們要操作GPIOE的ODR寄存器的話就可以通過“GPIOE->ODR”來實現,這個方法是借助了結構體成員地址連續遞增的原理。
了解了STM32的寄存器定義以后,我們就可以參考其原理來編寫I.MX6U的外設寄存器定義了。NXP官方并沒有為I.MX6UL編寫類似stm32f10x.h這樣的文件,NXP只為I.MX6ULL提供了類似stm32f10x.h這樣的文件,名為MCIMX6Y2.h,但是I.MX6UL和I.MX6ULL幾乎一模一樣,所以文件MCIMX6Y2.h可以用在I.MX6UL上。關于文件MCIMX6Y2.h的移植我們在下一章講解,本章我們參考stm32f10x.h來編寫一個簡單的MCIMX6Y2.h文件。
11.1.2 I.MX6U寄存器定義
參考STM32的官方文件來編寫I.MX6U的寄存器定義,比如IO復用寄存器組“IOMUX_SW_MUX_CTL_PAD_XX”,步驟如下:
1、編寫外設結構體
先將同屬于一個外設的所有寄存器編寫到一個結構體里面,如IO復用寄存器組的結構體如下:
示例代碼11.1.2.1 寄存器IOMUX_SW_MUX_Type
/*
* IOMUX寄存器組
*/
1typedefstruct
2{
3 volatileunsignedint BOOT_MODE0;
4 volatileunsignedint BOOT_MODE1;
5 volatileunsignedint SNVS_TAMPER0;
6 volatileunsignedint SNVS_TAMPER1;
………
107 volatileunsignedint CSI_DATA00;
108 volatileunsignedint CSI_DATA01;
109 volatileunsignedint CSI_DATA02;
110 volatileunsignedint CSI_DATA03;
111 volatileunsignedint CSI_DATA04;
112 volatileunsignedint CSI_DATA05;
113 volatileunsignedint CSI_DATA06;
114 volatileunsignedint CSI_DATA07;
/*為了縮短代碼,其余IO復用寄存器省略 */
115}IOMUX_SW_MUX_Tpye;
上述結構體IOMUX_SW_MUX_Type就是IO復用寄存器組,成員變量是每個IO對應的復用寄存器,每個寄存器的地址是32位,每個成員都使用“volatile”進行了修飾,目的是防止編譯器優化。
2、定義IO復用寄存器組的基地址
根據結構體IOMUX_SW_MUX_Type的定義,其第一個成員變量為BOOT_MODE0,也就是BOOT_MODE0這個IO的IO復用寄存器,查找I.MX6U的參考手冊可以得知其地址為0X020E0014,所以IO復用寄存器組的基地址就是0X020E0014,定義如下:
#define IOMUX_SW_MUX_BASE (0X020E0014)
3、定義訪問指針
訪問指針定義如下:
#define IOMUX_SW_MUX ((IOMUX_SW_MUX_Type *)IOMUX_SW_MUX_BASE)
通過上面三步我們就可以通過“IOMUX_SW_MUX->GPIO1_IO03”來訪問GPIO1_IO03的IO復用寄存器了。同樣的,其他的外設寄存器都可以通過這三步來定義。
11.2 硬件原理分析
本章使用到的硬件資源和第八章一樣,就是一個LED0。
11.3實驗程序編寫
本實驗對應的例程路徑為:開發板光盤-> 1、裸機例程->3_ledc_stm32。
創建VSCode工程,工作區名字為“ledc_stm32”,新建三個文件:start.S、main.c和imx6ul.h。其中start.S是匯編文件,start.S文件的內容和第十章的start.S一樣,直接復制過來就可以。main.c 和imx6ul.h是C文件,完成以后如圖11.3.1所示:
圖11.3.1工程文件目錄
文件imx6ul.h用來存放外設寄存器定義,在imx6ul.h中輸入如下代碼:
示例代碼11.2.1 imx6ul.h文件代碼
/***************************************************************
Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : imx6ul.h
作者 : 左忠凱
版本 : V1.0
描述 : IMX6UL相關寄存器定義,參考STM32寄存器定義方法
其他 : 無
日志 : 初版V1.0 2019/1/3 左忠凱創建
**************************************************************/
/*
* 外設寄存器組的基地址
*/
1 #define CCM_BASE (0X020C4000)
2 #define CCM_ANALOG_BASE (0X020C8000)
3 #define IOMUX_SW_MUX_BASE (0X020E0014)
4 #define IOMUX_SW_PAD_BASE (0X020E0204)
5 #define GPIO1_BASE (0x0209C000)
6 #define GPIO2_BASE (0x020A0000)
7 #define GPIO3_BASE (0x020A4000)
8 #define GPIO4_BASE (0x020A8000)
9 #define GPIO5_BASE (0x020AC000)
10
11/*
12 * CCM寄存器結構體定義,分為CCM和CCM_ANALOG
13 */
14typedefstruct
15{
16volatileunsignedint CCR;
17volatileunsignedint CCDR;
18volatileunsignedint CSR;
……
46volatileunsignedint CCGR6;
47volatileunsignedint RESERVED_3[1];
48volatileunsignedint CMEOR;
49} CCM_Type;
50
51typedefstruct
52{
53volatileunsignedint PLL_ARM;
54volatileunsignedint PLL_ARM_SET;
55volatileunsignedint PLL_ARM_CLR;
56volatileunsignedint PLL_ARM_TOG;
……
110volatileunsignedint MISC2;
111volatileunsignedint MISC2_SET;
112volatileunsignedint MISC2_CLR;
113volatileunsignedint MISC2_TOG;
114} CCM_ANALOG_Type;
115
116/*
117 * IOMUX寄存器組
118 */
119typedefstruct
120{
121volatileunsignedint BOOT_MODE0;
122volatileunsignedint BOOT_MODE1;
123volatileunsignedint SNVS_TAMPER0;
……
241volatileunsignedint CSI_DATA04;
242volatileunsignedint CSI_DATA05;
243volatileunsignedint CSI_DATA06;
244volatileunsignedint CSI_DATA07;
245}IOMUX_SW_MUX_Type;
246
247typedefstruct
248{
249volatileunsignedint DRAM_ADDR00;
250volatileunsignedint DRAM_ADDR01;
……
419volatileunsignedint GRP_DDRPKE;
420volatileunsignedint GRP_DDRMODE;
421volatileunsignedint GRP_DDR_TYPE;
422}IOMUX_SW_PAD_Type;
423
424/*
425 * GPIO寄存器結構體
426 */
427typedefstruct
428{
429volatileunsignedint DR;
430volatileunsignedint GDIR;
431volatileunsignedint PSR;
432volatileunsignedint ICR1;
433volatileunsignedint ICR2;
434volatileunsignedint IMR;
435volatileunsignedint ISR;
436volatileunsignedint EDGE_SEL;
437}GPIO_Type;
438
439
440/*
441 * 外設指針
442 */
443 #define CCM ((CCM_Type *)CCM_BASE)
444 #define CCM_ANALOG ((CCM_ANALOG_Type *)CCM_ANALOG_BASE)
445 #define IOMUX_SW_MUX ((IOMUX_SW_MUX_Type *)IOMUX_SW_MUX_BASE)
446 #define IOMUX_SW_PAD ((IOMUX_SW_PAD_Type *)IOMUX_SW_PAD_BASE)
447 #define GPIO1 ((GPIO_Type *)GPIO1_BASE)
448 #define GPIO2 ((GPIO_Type *)GPIO2_BASE)
449 #define GPIO3 ((GPIO_Type *)GPIO3_BASE)
450 #define GPIO4 ((GPIO_Type *)GPIO4_BASE)
451 #define GPIO5 ((GPIO_Type *)GPIO5_BASE)
在編寫寄存器組結構體的時候注意寄存器的地址是否連續,有些外設的寄存器地址可能不是連續的,會有一些保留地址,因此我們需要在結構體中留出這些保留的寄存器。比如CCM的CCGR6寄存器地址為0X020C4080,而寄存器CMEOR的地址為0X020C4088。按照地址順序遞增的原理,寄存器CMEOR的地址應該是0X020C4084,但是實際上CMEOR的地址是0X020C4088,相當于中間跳過了0X020C4088-0X020C4080=8個字節,如果寄存器地址連續的話應該只差4個字節(32位),但是現在差了8個字節,所以需要在寄存器CCGR6和CMEOR直接加入一個保留寄存器,這個就是“示例代碼11.3.1”中第47行RESERVED_3[1]的來源。如果不添加保留為來占位的話就會導致寄存器地址錯位!
main.c文件中輸入如下所示內容:
示例代碼11.3.2 main.c文件代碼
1 #include "imx6ul.h"
2
3/*
4 * @description : 使能I.MX6U所有外設時鐘
5 * @param : 無
6 * @return : 無
7 */
8void clk_enable(void)
9{
10 CCM->CCGR0 =0XFFFFFFFF;
11 CCM->CCGR1 =0XFFFFFFFF;
12 CCM->CCGR2 =0XFFFFFFFF;
13 CCM->CCGR3 =0XFFFFFFFF;
14 CCM->CCGR4 =0XFFFFFFFF;
15 CCM->CCGR5 =0XFFFFFFFF;
16 CCM->CCGR6 =0XFFFFFFFF;
17}
18
19/*
20 * @description : 初始化LED對應的GPIO
21 * @param : 無
22 * @return : 無
23 */
24void led_init(void)
25{
26/* 1、初始化IO復用 */
27 IOMUX_SW_MUX->GPIO1_IO03 =0X5;/* 復用為GPIO1_IO03 */
28
29
30/* 2、配置GPIO1_IO03的IO屬性
31 *bit 16:0 HYS關閉
32 *bit [15:14]: 00 默認下拉
33 *bit [13]: 0 kepper功能
34 *bit [12]: 1 pull/keeper使能
35 *bit [11]: 0 關閉開路輸出
36 *bit [7:6]: 10 速度100Mhz
37 *bit [5:3]: 110 R0/6驅動能力
38 *bit [0]: 0 低轉換率
39 */
40 IOMUX_SW_PAD->GPIO1_IO03 =0X10B0;
41
42
43/* 3、初始化GPIO */
44 GPIO1->GDIR =0X0000008;/* GPIO1_IO03設置為輸出 */
45
46/* 4、設置GPIO1_IO03輸出低電平,打開LED0 */
47 GPIO1->DR &=~(1<<3);
48
49}
50
51/*
52 * @description : 打開LED燈
53 * @param : 無
54 * @return : 無
55 */
56void led_on(void)
57{
58/* 將GPIO1_DR的bit3清零 */
59 GPIO1->DR &=~(1<<3);
60}
61
62/*
63 * @description : 關閉LED燈
64 * @param : 無
65 * @return : 無
66 */
67void led_off(void)
68{
69/* 將GPIO1_DR的bit3置1 */
70 GPIO1->DR |=(1<<3);
71}
72
73/*
74 * @description : 短時間延時函數
75 * @param - n : 要延時循環次數(空操作循環次數,模式延時)
76 * @return : 無
77 */
78void delay_short(volatileunsignedint n)
79{
80while(n--){}
81}
82
83/*
84 * @description : 延時函數,在396Mhz的主頻下
85 * 延時時間大約為1ms
86 * @param - n : 要延時的ms數
87 * @return : 無
88 */
89void delay(volatileunsignedint n)
90{
91while(n--)
92{
93 delay_short(0x7ff);
94}
95}
96
97/*
98 * @description : mian函數
99 * @param : 無
100 * @return : 無
101 */
102int main(void)
103{
104 clk_enable(); /* 使能所有的時鐘 */
105 led_init(); /* 初始化led */
106
107while(1) /* 死循環 */
108{
109 led_off(); /* 關閉LED */
110 delay(500); /* 延時500ms */
111
112 led_on(); /* 打開LED */
113 delay(500); /* 延時500ms */
114}
115
116return0;
117}
main.c中7個函數,這7個函數的含義和第十章中的main.c文件一樣,只是函數體寫法變了,寄存器的訪問采用imx6ul.h中定義的外設指針。比如第27行設置GPIO1_IO03的復用功能就可以通過“IOMUX_SW_MUX->GPIO1_IO03”來給寄存SW_MUX_CTL_PAD_GPIO1_IO03賦值。
11.4編譯下載驗證
11.4.1 編寫Makefile和鏈接腳本
Makefile文件的內容基本和第十章的Makefile一樣,如下:
示例代碼11.4.1 Makefile文件代碼
1 objs:= start.o main.o
2
3 ledc.bin:$(objs)
4 arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^
5 arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
6 arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
7
8 %.o:%.s
9 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
10
11 %.o:%.S
12 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
13
14 %.o:%.c
15 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
16
17 clean:
18 rm -rf *.o ledc.bin ledc.elf ledc.dis
鏈接腳本imx6ul.lds的內容和上一章一樣,可以直接使用上一章的鏈接腳本文件。
11.4.2編譯下載
使用Make命令編譯代碼,編譯成功以后使用軟件imxdownload將編譯完成的ledc.bin文件下載到SD卡中,命令如下:
chmod 777 imxdownload //給予imxdownload可執行權限,一次即可
./imxdownload ledc.bin /dev/sdd //燒寫到SD卡中
燒寫成功以后將SD卡插到開發板的SD卡槽中,然后復位開發板,如果代碼運行正常的話LED0就會以500ms的時間間隔亮滅,實驗現象和上一章一樣。
總結
以上是生活随笔為你收集整理的stm32官方例程在哪找_正点原子Linux第十一章模仿STM32驱动开发格式实验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: truncate数据后回收空间_Trun
- 下一篇: python的表达式3or5_Pytho