int a = 100; * (&a) = 100; 0xc001c000 // 常量 (int*)0xc001c000 // 地址 * ((int*)0xc001c000) = 0x100; // 寫入數據,按int-4字節寫入0x100volatile 的作用,以及哪種場合需要使用該關鍵字?(課后查資料)>>表示'一個變量也許會被后臺程序意想不到的修改'。變量如果加了volatile修飾,則會從內存重新裝載內容,而不是直接從寄存器拷貝內容。volatile可以保證對特殊地址的穩定訪問。>>寄存器地址要加volatile修飾,主要是因為寄存器里面的值是隨時變化的。 我們讀取數據的時候,CPU直接到內存里面取值,而不是到cache里面。 1) 并行設備的硬件寄存器; 2) 中斷服務子程序中會訪問到的非自動變量; 3) 多線程應用中被及格任務共享的變量;例如:
volatile int i = 10; int a = i; // 其他代碼,并未告訴編譯器,對i進行過操作 int b = i;"volatile 指出i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由于編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i里面讀,這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說 volatile 可以保證特殊地質的穩定訪問。"// 訪問特殊功能寄存器的時候需要加 volatile 關鍵字,正確的寫法: a = * ((volatile unsigned int*)0xc001c000); // 下面此條做法,不但將bit12清0,也導致其他的bit被清0了,錯誤!! * ((volatile unsigned int*)0xc001c000) = 0x00;3.2 位運算 假設需要將 0xc001c020 寄存器的[25:24]bit位設置為 01 ,* ((volatile unsigned int*)0xc001c020) &= ~(0x03000000); // 第一步 // 32bit : xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx // 0x03000000 0000 0011 0000 0000 0000 0000 0000 0000 // ~(0x03000000) 1111 1100 1111 1111 1111 1111 1111 1111 // &= ~(0x03000000) xxxx xx00 xxxx xxxx xxxx xxxx xxxx xxxx * ((volatile unsigned int*)0xc001c020) |= 0x01000000; // 第二步 // 32bit : xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx // 01 ([25:24]bit) // 0x0______01_0____0____0____0____0____0 "實際做法:" * ((volatile unsigned int*)0xc001c020) &= ~(3<<24); // 第一步 * ((volatile unsigned int*)0xc001c020) |= (1<<24); // 第二步<tips>$:'file a.out// file 命令可以用來分析文件的一些屬性。x86-64 是x86的64位平臺運行。4. 編碼 vi led.c/** 代碼演示 **/#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)void delay (unsigned int); void led_test (void) { // 配置對應管腳為GPIO功能 GPIOC_ALTFN0 &= ~ (3 << 24); // clear bit 24,25 GPIOC_ALTFN0 |= (1 << 24); // set bit 24 // 選擇為輸出功能 GPIOC_OUTENB |= (1 << 12); // OUTPUT while (1) { // 亮 - 輸出低電平 GPIOC_OUT &= ~ (1 << 12); // clear bit 12 delay (0x1000000); // 滅 - 輸出高電平 GPIOC_OUT |= (1 << 12); // set bit 12 delay (0x1000000); }}// delay函數的實現不能放前面,只能先聲明后實現void delay (unsigned int n) { unsigned int i = 0; for (i = n; i != 0; i--); // CPU執行空操作來耗時}5. arm編譯器
5.1 安裝arm交叉編譯器>>位置:/home/tarena/workdir/arm-linux-gcc/ 'arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz 權限不夠時解決方法: 1> $:' sudo ... 2> $:' sudo chmod +w /opt/ -R 3> $:' sudo chmod 777 /opt/ -R // 將交叉編譯器tar包文件拷貝到/opt/下,在此文件夾下解壓縮 $:'cd ~/opt/ $:'sudo cp ~/workdir/arm..<table>/arm... .tar.gz . $:'sudo tar xvf *.gz>>位置:/opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin/ 'arm-cortex_a9-linux-gnueabi-gcc $:'vi ~/.bashrc>>添加:<shift + g> // 添加到文件尾 'PATH=$PATH:/opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin $:'source ~/.bashrc5.2 編譯程序希望編譯出來的程序不使用共享庫 (因為是裸板) 1)編譯 $:'arm-cortex_a9-linux-gnueabi-gcc -c -nostdlib led.c -o led.o 2)連接 $:'arm-cortex_a9-linux-gnueabi-ld -nostdlib -nostartfiles -Ttext=0x48000000 -eled_test led.o -o led // -no stdlib 不連接標準庫文件 // -no startfiles 不連接系統標準啟動文件 // -Ttext 指定代碼段的起始地址 // -e 指定入口點函數,默認找_start 3)去掉附加信息,生成二進制文件 $:'arm-cortex_a9-linux-gnueabi-objcopy -O binary led led.bin $:' file led.bin<tips>$:'arm-cortex_a9-linux-gnueabi-readelf -d a.out// 顯示可執行文件a.out文件中所需的共享庫.so信息$:'arm-cortex_a9-linux-gnueabi-objdump -S led// 反匯編的命令,查看文件的具體的函數及信息6. 下載運行: X6818# 命令行 $:' cp led.bin /tftpboot/ X6818#:'tftp 48000000 led.bin // uboot下數字都識別為十六進制 X6818#:' go led.bin<tips>執行tftp 48000000 led.bin 時 Ubuntu-server報 T..T..T...時需重啟服務器:$:'sudo /etc/init.d/tftpd-hpa restart補充:編程錯誤>>語法錯誤: '只關注只解決第一個錯誤。邏輯錯誤: ' printf / gdb 1. 是不是硬件問題? env/led.bin放進去,燈亮硬件沒問題(或者換到別人電腦上試一試) 2. 如果是軟件問題,怎么查? 修改led.c的邏輯 讓對應管腳一直輸出高電平 拿萬用表量一下對應的管腳上是否是高電平。env/BCompare-3.3.4.14431.zip // 工具,對比文件夾和文件里面不同之處<tips>vi中的命令模式下:e ../xxx.c // 在vi中直接打開別的文件練習: 1. LED1和LED2交替閃爍。(理清楚今天的整個流程,看手冊,理流程) 2. beep蜂鳴器的控制管腳相關數據準備:'led1 - GPIOC12 - W15 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[25:24] - 01=Function1 (I/O功能) GPIOCOUTENB - 0xc001c004 - bit[12] - 1=Output Mode (輸出功能) GPIOCOUT - 0xc001c000 - bit[12] - 1=High Level (0/1 低高電平)'led2 - GPIOC7 - AE21 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[15:14] - 01 GPIOCOUTENB - 0xc001c004 - bit[7] - 1 GPIOCOUT - 0xc001c000 - bit[7] - 1'led3 - GPIOC11 - W14 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[23:22] - 01 GPIOCOUTENB - 0xc001c004 - bit[11] - 1 GPIOCOUT - 0xc001c000 - bit[11] - 1'led4 - GPIOB26 - MCU_SPI_WP AC25 - Function1 (暫略)/** 代碼演示 **/#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)void delay (unsigned int);void led_run (void) { // 配置GPIOC管腳 // led1 25:24 GPIOC_ALTFN0 &= ~ (3 << 24); GPIOC_ALTFN0 |= (1 << 24); // led2 15:14 GPIOC_ALTFN0 &= ~ (3 << 14); GPIOC_ALTFN0 |= (1 << 14); // led3 23:22 GPIOC_ALTFN0 &= ~ (3 << 22); GPIOC_ALTFN0 |= (1 << 22); // 設置輸出功能 GPIOC_OUTENB |= (1 << 12); GPIOC_OUTENB |= (1 << 11); GPIOC_OUTENB |= (1 << 7); for (;;) { GPIOC_OUT &= ~ (1 << 12); // led1 delay (0x800000); GPIOC_OUT &= ~ (1 << 11); // led2 delay (0x800000); GPIOC_OUT &= ~ (1 << 7); // led3 delay (0x800000); GPIOC_OUT |= (1 << 12); delay (0x800000); GPIOC_OUT |= (1 << 11); delay (0x800000); GPIOC_OUT |= (1 << 7); delay (0x800000); } }void delay (unsigned int n) { unsigned int i; for (i = n; i; --i);}2. beep蜂鳴器的控制' beep蜂鳴器 - PWM2[GPIOC14] - AD12 - Function2 GPIOCALTFN0 - 0xc001c020 - bit[29:28] - 10=Function2 (I/O功能) GPIOCOUTENB - 0xc001c004 - bit[14] - 1=Output Mode (輸出功能) GPIOCOUT - 0xc001c000 - bit[14] - 1=High Level (0/1 低高電平)
/** 代碼演示 **/#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)void delay (unsigned int);void beep_run (void) { // 配置GPIO管腳 GPIOC_ALTFN0 &= ~(3 << 28); GPIOC_ALTFN0 |= (2 << 28); // 設置輸出功能 GPIOC_OUTENB |= (1 << 14); for (;;) { GPIOC_OUT &= ~(1 << 14); // 低電平 - 鳴叫 delay (0x2000000); GPIOC_OUT |= (1 << 14); // 高電平 - 不叫 (無延時和停止蜂鳴效果) delay (0x2000000); } }void delay (unsigned int n) { unsigned int i; for (i = n; i; --i);}
新聞熱點
疑難解答