一、問題描述
在學(xué)習(xí)野火霸天虎F407寄存器點(diǎn)亮LED時(shí),出現(xiàn)實(shí)驗(yàn)現(xiàn)象:LED燈不亮,野火霸天虎F407資料。
main.c代碼如下:
#include "stm32f4xx.h"
void Delay(unsigned int count);
int main(void)
{
#if 0
/* 第一步:開啟GPIO端口的時(shí)鐘 */
/* 打開GPIOF端口的時(shí)鐘 */
*(unsigned int *)(0x40023800+0x3f0) |= (1<<5);
/* 第二步:控制GPIO的方向 */
/* GPIOF 配置為輸出 */
*(unsigned int *)(0x40021400+0x00) &= ~((0x03) << (2*6));
*(unsigned int *)(0x40021400+0x00) |= (1 << (2*6));
/* 第三步:控制GPIO的數(shù)據(jù)輸出寄存器 */
/* PF6 輸出高電平 */
*(unsigned int *)(0x40021400+0x14) |= (1 << 6);
/* PF6 輸出低電平 */
*(unsigned int *)(0x40021400+0x14) &= ~(1 << 6);
#elif 0
/* 第一步:開啟GPIO端口的時(shí)鐘 */
/* 打開GPIOF端口的時(shí)鐘 */
RCC_AHB1ENR |= (1<<5);
/* 第二步:控制GPIO的方向 */
/* GPIOF 配置為輸出 */
GPIO_MODER &= ~((0x03) << (2*6));
GPIO_MODER |= (1 << (2*6));
/* 第三步:控制GPIO的數(shù)據(jù)輸出寄存器 */
/* PF6 輸出高電平 */
GPIO_ODR |= (1 << 6);
/* PF6 輸出低電平 */
GPIO_ODR &= ~(1 << 6);
#elif 0
//任務(wù)1-把其他兩個(gè)燈也點(diǎn)亮
RCC_AHB1ENR |= (1<<5); //開啟GPIO端口時(shí)鐘
//設(shè)置GPIOF6為推挽輸出
GPIO_MODER &= ~((0x03) << (2*6));
GPIO_MODER |= (1 << (2*6));
GPIO_ODR |= (1 << 6);
GPIO_ODR &= ~(1 << 6);
//設(shè)置GPIOF7為推挽輸出
GPIO_MODER &= ~((0x03) << (2*7));
GPIO_MODER |= (1 << (2*7));
GPIO_ODR |= (1 << 7);
GPIO_ODR &= ~(1 << 7);
//設(shè)置GPIOF8為推挽輸出
GPIO_MODER &= ~((0x03) << (2*8));
GPIO_MODER |= (1 << (2*8));
GPIO_ODR |= (1 << 8);
GPIO_ODR &= ~(1 << 8);
#elif 1
//任務(wù)1-把其他兩個(gè)燈也點(diǎn)亮
RCC_AHB1ENR |= (1<<5); //開啟GPIO端口時(shí)鐘
//設(shè)置GPIOF6為推挽輸出
GPIO_MODER &= ~((0x03) << (2*6));
GPIO_MODER |= (1 << (2*6));
//設(shè)置GPIOF7為推挽輸出
GPIO_MODER &= ~((0x03) << (2*7));
GPIO_MODER |= (1 << (2*7));
//設(shè)置GPIOF8為推挽輸出
GPIO_MODER &= ~((0x03) << (2*8));
GPIO_MODER |= (1 << (2*8));
while(1)
{
GPIO_ODR &= ~(1 << 6);
Delay(0xfffff);
GPIO_ODR |= (1 << 6);
Delay(0xfffff);
GPIO_ODR &= ~(1 << 7);
Delay(0xfffff);
GPIO_ODR |= (1 << 7);
Delay(0xfffff);
GPIO_ODR &= ~(1 << 8);
Delay(0xfffff);
GPIO_ODR |= (1 << 8);
Delay(0xfffff);
}
#endif
}
//延時(shí)函數(shù)
void Delay(unsigned int count)
{
for(;count!=0;count--);
}
void SystemInit(void)
{
/* 函數(shù)體為空,目的是為了騙過編譯器不報(bào)錯(cuò) */
}
/*
1-把其他兩個(gè)燈也點(diǎn)亮
2-實(shí)現(xiàn)三個(gè)燈閃爍(時(shí)間的控制使用軟件延時(shí))
*/
二、問題分析
通過分析main.c代碼,導(dǎo)致出現(xiàn)上述現(xiàn)象的間接原因是延時(shí)函數(shù)沒有起作用。檢查延時(shí)函數(shù)的實(shí)現(xiàn)代碼,并沒有錯(cuò)誤。這不禁使我想起《程序員的自我修養(yǎng)——鏈接、裝載、庫》一書所提到的程序源代碼經(jīng)過預(yù)編譯-》編譯-》匯編-》鏈接,所以極大可能是編譯器在編譯過程中優(yōu)化掉了我的延時(shí)函數(shù),使得整個(gè)程序不能按照預(yù)定功能實(shí)現(xiàn)。
打開keil5的調(diào)試功能,查看對應(yīng)main.c的反匯編文件:
經(jīng)過優(yōu)化的delay函數(shù):
未經(jīng)過優(yōu)化的delay函數(shù):
優(yōu)化之后的 delay 函數(shù)沒有for循環(huán)延時(shí)操作,因此失去延時(shí)的效果。
三、問題解決
3.1 降低ARM Compiler version
在Target設(shè)置界面下,Code Generation默認(rèn)的是ARM Compiler version 6。
將ARM Compiler version 6改為ARM Compiler version 5即可。
3.2 volatile關(guān)鍵字修飾
volatile關(guān)鍵字影響編譯器編譯的結(jié)果,用volatile聲明的變量表示該變量隨時(shí)可能發(fā)生變化,與該變量有關(guān)的運(yùn)算,不要進(jìn)行編譯優(yōu)化,以免出錯(cuò)。volatile關(guān)鍵字最通俗的解釋是,告訴編譯器這個(gè)變量我有其他用,不要給我隨便優(yōu)化掉。
原延時(shí)函數(shù)
//延時(shí)函數(shù)
void Delay(unsigned int count)
{
for(;count!=0;count--);
}
添加volate關(guān)鍵字修飾
//延時(shí)函數(shù)
void Delay(volatile unsigned int count)
{
for(;count!=0;count--);
}
推薦使用方法2,原因如下:
1.自定義延時(shí)函數(shù)中使用 volatile 去聲明 val 變量可以解決編譯器優(yōu)化帶來的延時(shí)失效問題;
2.編譯器優(yōu)化可以使代碼更加精煉,執(zhí)行效率更高。文章來源:http://www.zghlxwxcb.cn/news/detail-518067.html
參考資料
1. Keil AC5 和 AC6的一些區(qū)別
2. 編譯器優(yōu)化對自定義延時(shí)程序的影響(volatile詳解實(shí)驗(yàn)一)
3. C語言丨深入理解volatile關(guān)鍵字文章來源地址http://www.zghlxwxcb.cn/news/detail-518067.html
到了這里,關(guān)于Keil5中寫的軟件延時(shí)函數(shù)不起作用現(xiàn)象解析_ARM_Compiler_volatile關(guān)鍵字的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!