@藍橋杯第十屆國賽部分功能決議TOC
藍橋杯單片機第十屆國賽 部分功能決議
備注: 這是本人第一次發表的文章,內容有不足、有問題、有改進的地方請在評論區留言 (主要供給個人復習,隨手一寫,對大佬無用請轉走)
按鍵部分:
下降沿代碼
key_number = key_trigger();
key_change = (key_number ^ Key_old) & key_number;
Key_old = key_number;
(這三行代碼是從藍橋杯官方指導書上學到的,非常非常實用,)
備注:
key_trigger() 負責對按下的按鍵進行采集并回傳,
key_number 負責讀取key_trigger()的回傳值,
key_change 負責記錄下降沿觸發的按鍵,
key_old 負責記錄按鍵的上一個狀態
拿例子說話:
當有按鍵’4’按下時:
key_number = 4;
key_old = 0;
key_change = (4 ^ 0) & 4;
key_old = 4;
那么key_change的值 就為:4
(key_change 就得到了一次按下的按鍵值)
這是單片機在有按鍵按下時的第一次掃描,第二次掃描的變化及以后:
key_number = 4;
key_old = 4;
key_change = (4 ^ 4) & 4;
key_old = 4;
得到的key_change結果就是:0
(這時候如果我們一直按著不松手得到的key_change結果也就一直是 0,呢就會有人懷疑為什么 & key_number,這一步到底有什么用?答案就在下面,)
當我們松手時:
key_number = 0;
key_old = 4;
key_change = (0 ^ 4) & 0;
key_old = 0;
我們仔細觀察這個 & key_number 這步操作,發現 key_change 仍然為 0 試想一下如果沒有這步操作將會是什么?
理解了以后也就解決了上面的問題,我們發現如果觸發一個按鍵以后 key_change僅僅會得到一次按下的鍵值,其余都是0,也就得到了下降沿的檢測;
擴展 上升沿
key_change = (~key_number) & (key_number ^ Key_old);
這里沒有使用到,所以不再贅述,可以按照如上方法進行驗證
長按按鍵(1s)+ 按鍵匯總
代碼如下:
void timer0() interrupt 1 //12mhz 1ms
{
if(key_delay) key_delay--;
}
void key_pricedure()
{
if(key_slow_down) return; //減速
key_slow_down = 1; //此行代碼是為了提高效率 在規定時間內僅進入一次,防止多次進入
key_number = key_trigger();
key_change = (key_number ^ Key_old) & key_number;
Key_old = key_number; //上面對此部分進行了決議
switch(key_change)
{
case 8:
key_delay = 1000;
//......下面部分加入按鍵按下時操作的代碼,且長按不會影響下面的內容
}
if((key_number == 8) && (key_delay == 0)) //按鍵8觸發 同時保持了1s
{
key_delay = 1000; //1s進入一次長按代碼 且短按代碼只進入一次
//......下面部分加入按鍵長按的代碼
//DAC_output_flag ^= 1; 例子
}
}
(如果我們按下S8按鍵,使switch陳述句中的case 8執行一次,)
“按下時間” < 1s:
在一秒以內 “key_number = 0” ,此時 key_delay 還在不斷的進行"-- --"操作,等減到0的時候,key_number早就變成了0 逃之夭夭,
那么程式也就不會執行到長按的代碼 ,
“按下時間” >= 1s:
當按下S8以后1S內不松手,也就代表著key_delay = 0 的時候此時 key_number = 8;也就符合長按要求,
但應注意根據需求加 key_delay = 1000;
例如:上面的 DAC_output_flag ^= 1 ; 如果沒有加key_delay = 1000; 進入長按程式后就會使這個標志位一直在翻轉,你根本不知道你松手以后會變成0\1;加了以后就是1S進入一次長按,翻轉一次,
如果我們需要長按后僅僅觸發一次,那么最笨的方法就是進入長按函式后加大key_delay 的值,使下一次進入增大到很長時間,( 當然也可以增加標志位來操作,效果都一樣,)
當我們在松手的時候雖然key_delay 可能還在一直“減減”但也無傷大雅,因為我們按下S8按鍵以后仍然會對其重新賦值!
超聲波部分:
代碼如下:
sbit csb_TI = P1^0;
sbit csb_RI = P1^1;
void timer1_init()
{
AUXR &= 0XBF;
TMOD &= 0X0F;
TCON |= 0X40;
TL1 = 0xF4; //設定定時初值
TH1 = 0xFF; //設定定時初值
}
unsigned int csb_distance_fun() //最大距離99
{
unsigned char number = 10; //發送5個周期的超聲波
unsigned int temp;
TL1 = 0xF4; //設定定時初值
TH1 = 0xFF; //設定定時初值
csb_TI = 0;
while(number--) //控制翻轉次數 10次 也就是五個周期,發送5個周期的超聲波
{
while(!TF1); //12ms會使csb_TI 進行一次翻轉 也就制造了 4000hz的頻率
TF1 = 0;
csb_TI ^= 1;
}
TR1 = 0; //如果不關掉定時器,由于12ms很快 可能會使定時器1出發標志為 ‘1’ 那么下面就影響了(這段解釋有瑕疵)
TH1 = 0x00;
TL1 = 0x00;
TR1 = 1;
while(!TF1 && csb_RI); //檢測是否接收到超聲波信號 如果有那么csb_RI會被硬體拉低 或者就是長時間未接收到超聲波信號都會打破死回圈,
if(TF1) //是否是時間長未接收到超聲波信號,這個時間長度是 65536ms 因為上面使TH1和TL1 都為0了
{
TF1 = 0;
temp = 99;
}
else //如果接收到超聲波信號
{
temp = (TH1 << 8) + TL1;
temp = (unsigned int)(temp * 0.017); //距離計算 = 聲速 * 時間 / 2
if(temp > 99) temp = 99;
}
return temp;
}
超聲波測距原理上面的備注已經比較詳細了,這里不再贅述,
- 但是在超聲波部分我遇到了一個問題:
temp = (TH1 << 8) + TL1;
temp = (unsigned int)(temp * 0.017); //距離計算 = 聲速 * 時間 / 2
如果換成
temp = (((TH1 << 8) + TL1)* 17)/ 1000;
以后會出現問題
大概就是如果超過65內部就會進位 比如66就成了0,67成了1…一直沒有弄清楚原因,有知道的大佬請留言,萬分感謝,
串口部分:
本部分主要也是按照官方思路走的,總之官方代碼yyds!
void uart1() interrupt 4
{
if(RI == 1)
{
uart_read_number[uart_i_read++] = SBUF;
RI = 0;
}
}
if(uart_read_number[uart_i_read-1] == '\n')
{
if((uart_read_number[0] == 'S') && (uart_read_number[1] == 'T')&& (uart_read_number[2] == '\r'))
{
//......
}
else if((uart_read_number[0] == 'P' && uart_read_number[1] == 'A' && uart_read_number[2] == 'R' &&
uart_read_number[3] == 'A' && uart_read_number[4] == '\r'))
{
//.......
}
else
{
//.......
}
uart_i_read = 0;
}
else if(uart_i_read == 6)
{
uart_send_fun("ERROR\r\n"); //切記這里發送的是字串
uart_i_read = 0;
}
由于我們每次有效指令的結尾都是‘\n’這也就是個突破口,我們只要檢測’\n’來了以后,再進行判斷指令的內容是什么,而后進行對于操作即可,
這種操作簡單,清晰!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/282654.html
標籤:其他
