沒事做突然想寫博客,然後發現沒有刷什麼有價值的題,就寫個做實驗的記錄吧。
首先是構思的思維導圖
可以看到,我用到了按鍵結合中斷作爲輸入,使用定時中斷來計數,同時顯示使用的是oled屏幕,大小爲128*64個像素點。
屏幕圖片源水印淘寶店( ̄▽ ̄)”
ok開始第一步,配置管腳圖
oled管腳*7,一個3v3,一個GND,還有五個GPIO輸出。
這邊用的是串行通信的方法,各個管腳功能分別是
CS:OLED 片選信號。
RST(RES):硬復位 OLED。
DC:命令/數據標誌(0,讀寫命令;1,讀寫數據)。
SCLK:串行時鐘線。在 4 線串行模式下,D0 信號線作爲串行時鐘線 SCLK。
SDIN:串行數據線。在 4 線串行模式下,D1 信號線作爲串行數據線 SDIN。
oled管腳:
CS~PA0
DC/RS~PA2 //這邊的DC管腳在程序裏面被記作了RS,都一樣
RST~PA1
SCLK/D0~PB5
SDIN/D1~PB6
按鍵管腳*5,一個5v,一個GND,3個GPIO輸入。
按鍵功能爲
key0:開始和暫停計時
key1:重置計時
key2:計次,多人跑步的時候記下各個人情況
按鍵管腳:
key0~PB0
key1~PB1
key2~PB2
ok,既然心裏有B數了,那麼就可以開始設置初始化函數了
KEY_INIT(),我設置爲上拉輸入了,管腳改一下,就不多說了。
EXTI_INIT(),從按鍵設置可以看到,應該選中斷線0,1,2,配置管腳爲下降沿觸發,設置NVIC中斷優先級,中斷服務程序先留着。
計時器初始化,這邊用三號定時中斷,TIM3_Int_Init(100,7199),解釋一下,這個板子的時鐘頻率爲72Mhz,我們不需要這麼高的頻率來做定時器中斷,所以將他分頻,72M除一下設置的分頻值7199,得到的結果是1W,也就是現在我們的定時器中斷爲1Whz,我們想要計時器可以精確到0.01s,所以設置爲每數100次觸發一個定時器中斷。然後中斷服務程序就可以用來累加我們的時間了。
oled_init(),這個也就是按照之前的管腳配置做就行了,注意選擇串行通信模式
初始化完成,回過頭來想一下,我覺得中文顯示可以嗦一嗦
先找個軟件來,將中文轉爲16進制的點陣圖,放到oled_font裏面,我用了四個中文字
時間第名
,做成了四個數組,一個數組裏面有32個8位的16進制數。這邊用了一個OLED_DrawPoint(x,y,1)
,x和y爲像素點的位置,1表示這個點要點亮,否則用0。
另外,一個字爲16*16個像素點,這邊寫入的方式爲,先(0,0)->(0,1)->(0,2)……(0,15)->(1,0)->(1,2)…(15,15)。也就是先寫完一列16個像素,再換下一列。但是,我們數組中的數是8位的,也就是隻保存了8個像素點的狀態,所以一列要兩個數來表示。
void OLED_ShowMyChinese(u8 x,u8 y,u8 n)
{
u8 temp,t,t1;//c語言要先聲明變量,t,t1用來for循環,temp是取數組值
u8 y0=y; //保存列開頭位置
for(t=0;t<32;t++) //遍歷數組中32個數
{
temp=oled_chinese_1616[n][t]; //第n個漢字的第t個數
for(t1=0;t1<8;t1++) //遍歷一個數中的八個位
{
if(temp&0x80) //查看temp最高位,看像素點狀態
OLED_DrawPoint(x,y,1); //如果最高位是1,就點亮
else
OLED_DrawPoint(x,y,0); //否則暗着
temp<<=1; //左移查看下一個像素
y++; //移動到下一個像素點的物理位置
if((y-y0)==16) //這邊是判斷是否寫完一列
{ //畫完第一個8位數字後y不變回0
y=y0; //畫完兩個8位數字後y變回0
x++;
break;
}
}
}
}
阿西吧,終於到了最重要的部分了,計時部分開始啦
首先想一下,時間怎麼保存呢?當然是用結構體啦
struct StuTime{
u8 m,s;//八位的分鐘,秒
u16 ms;//16位的毫秒
}MyTime={0,0,0};//初始化爲0:0.00
寫在timer.c 這個文件最合適啦。這樣定時中斷服務就可以直接累加時間了。
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
if(!MyFlag) //flag用來看有不有暫停計時
return;
MyTime.ms+=10; //累加毫秒
if(MyTime.ms>=1000)//累加秒
{
MyTime.ms%=1000;
MyTime.s++;
if(MyTime.s>=60)//累加分鐘
{
MyTime.s%=60;
MyTime.m++;;
}
}
}
}
那麼暫停的方法上面也說了,用一個flag就行了
void MyTime_GO(void)
{
MyFlag=!MyFlag;
}
機智呀
這個函數將被外部中斷服務0程序調用
void EXTI0_IRQHandler(void)
{
delay_ms(10);
MyTime_GO();
EXTI_ClearITPendingBit(EXTI_Line0);
}
另外三個按鍵功能也是這樣的,所以主要的功能函數都在timer.c
這個文件裏面了
重置計時功能
void MyTime_RST(void)
{
MyFlag=0; //先把flag設爲0,暫停計時
MyTime.m=0; //再將時間全重置爲0
MyTime.s=0; //這樣以後呢,就不多忙活了
MyTime.ms=0;
}
然後是計次功能,顯示的樣式爲三行,這個oled可以寫四行字,這邊選擇下三行,第一行留給實時的時間。
這邊顯示的一行爲第nn名: nn:nn.nn
,這個n就是到時候要替換的數字了。
void MyTime_CNT(void)
{
u8 static MyCntFlag=1;//給這個函數加個鎖,防止連續觸發
u8 static MyCnt=0; //打算用三行來計次這個記錄我這是第幾次計次了,改寫哪行
u8 MyPageY;
if(!MyCntFlag) //查看有不有鎖上
return ;
MyCntFlag=0; //鎖上
MyPageY=(MyCnt%3+1)*16; //計算我改寫哪一行,也就是y的值
//顯示中文了
OLED_ShowMyChinese(0,MyPageY,2);//顯示我中文字庫的第二個字,就是"第"
OLED_ShowMyChinese(32,MyPageY,3);//顯示"名",和下面數字和在一起就是‘第n名’了
OLED_ShowNum(16,MyPageY,MyCnt+1,2,16);
//顯示分隔符
OLED_ShowChar(48,MyPageY,':',16,1);
OLED_ShowChar(80,MyPageY,':',16,1);
OLED_ShowChar(104,MyPageY,'.',16,1);
//替換數字,都是兩位的數字
OLED_ShowNum(64,MyPageY,MyTime.m,2,16);//分鐘
OLED_ShowNum(88,MyPageY,MyTime.s,2,16);//秒鐘
OLED_ShowNum(112,MyPageY,MyTime.ms,2,16);//毫秒
MyCnt++;//記下我寫過這行了,下次寫下一行
MyCntFlag=1;//記得把鎖打開
}
啊哈,到這邊計時已經實現了,可以暫停和重置還有計次,但是第一行的實時時間還沒有顯示,也沒有看到過刷新GRAM的函數,這樣是不行的,oled啥玩意都不會顯示
所以現在開始搞定最後一個main函數
首先,把七大姨八大叔全部叫來初始化一下,調用一個他們的init函數
然後進入永不退出的while(1)循環啦
第一行實時顯示的樣式爲時 間: nn:nn.nn
,同上,n爲要替換的數字
這邊用到了一個GetTime函數,先看一下這個函數,這個函數在timer.c
裏面
u32 GetTime(void)
{
return (u32)((MyTime.m<<24)+(MyTime.s<<16)+(MyTime.ms));
}
還記的我們的時間結構體嗎,8位的分鐘,8位的秒,16位的毫秒,加一起不就可以組成一個32位的數啦,這邊就用一個32位的數返回給要查看時間的同學了,前8位爲分鐘,後面8位爲秒,再最後16位就是毫秒了。
while(1)
{
u8 tmp;
u32 reVal=GetTime(); //從timer.c獲取時間
OLED_ShowMyChinese(0,0,0); //顯示‘時’
OLED_ShowMyChinese(32,0,1); //顯示‘間’
OLED_ShowChar(48,0,':',16,1); //顯示分隔的冒號
OLED_ShowChar(80,0,':',16,1);
OLED_ShowChar(104,0,'.',16,1);
tmp=(reVal&0xff000000)>>24;//根據前面GetTime的規則取出分鐘
OLED_ShowNum(64,0,tmp,2,16);//顯示分鐘
tmp=(reVal&0x00ff0000)>>16;//根據前面GetTime的規則取出秒
OLED_ShowNum(88,0,tmp,2,16);//顯示秒
tmp=(reVal&0x0000ffff);//根據前面GetTime的規則取出毫秒
OLED_ShowNum(112,0,tmp,2,16);//顯示毫秒
OLED_Refresh_Gram();//刷新oled,注意,整個項目中不要有多個刷新調用
delay_ms(10); //否則你的屏幕會亂掉,錯位
//這樣看來他的刷新函數應該沒有鎖保護了
}
}