【藍橋杯—單片機】第十五屆省賽真題代碼題解析 | 思路整理

第十五屆省賽真題代碼題解析

  • 前言
  • 賽題代碼思路筆記
    • 競賽板配置
    • 建立模板
    • 明確基本要求
    • 顯示功能部分
      • 頻率界面
        • 正常顯示
        • 高位熄滅
      • 參數界面
        • 基礎寫法:兩個界面分開來寫
        • 優化寫法:兩個界面合一起寫
      • 時間界面
      • 回顯界面
      • 校準
        • 校準過程
        • 校準錯誤顯示
    • DAC輸出部分
    • 按鍵功能部分
        • S4:“界面”按鍵
        • S5:“選擇”按鍵
          • 優化寫法
        • S8、S9:“加”按鍵 、“減”按鍵
      • LED指示燈功能部分
      • 調試時發現的問題
  • 最終代碼
    • User文件
      • main.c
    • Driver文件
      • init.c
      • Key.c
      • Led.c
      • Seg.c
      • iic.c
      • ds1302.c
    • 結語

前言

本文是對藍橋杯第十五屆省賽真題的代碼題做的解題筆記。
我在看完西風老師的解析視頻后,自己重新寫了一遍,做了點整理也加入了一點個人的思考,因此在細節上(比方說:變量名…)會有一點出入。

思路參考西風第二十三講內容(資料鏈接已經在下方給出)

https://www.bilibili.com/video/BV1TR4y1k7iz?p=23&vd_source=e2191f89c557f5ac44bb6c7aa3967c7c

關于藍橋杯第十五屆省賽真題可以在官網查看(資料鏈接已經在下方給出)

https://www.lanqiao.cn/courses/2786/learning/?id=2870978&compatibility=false

賽題代碼思路筆記

競賽板配置

在這里插入圖片描述

建立模板

根據賽題中的硬件框圖確定本賽題的框架
在這里插入圖片描述
如圖,在Driver文件夾里建立LED、數碼管、按鍵、iic模塊(DAC輸出)、DS1302,在User文件夾中建立主函數模塊(大模板參考西風老師的2024版大模板)。
在這里插入圖片描述

明確基本要求

在這里插入圖片描述
在這里插入圖片描述


注意1.的內容:

  • P34引腳和矩陣按鍵有沖突,寫按鍵模塊(Key.h)時注意屏蔽掉P34=0的情況
  • 板子的P34口和SIGNAL的跳帽要接上

在這里插入圖片描述
注意2.的內容:

  • 按鍵延遲時間要控制在100ms以內
	/*按鍵10ms的減速*/if(Slow_Down % 10 == 0){Key_Flag = 0;}

在這里插入圖片描述
注意第三點的內容:

  • 數碼管延遲時間要控制在100ms以內
    /*數碼管90ms的減速*/if(++Slow_Down == 90){Seg_Flag = Slow_Down = 0;}

在這里插入圖片描述

顯示功能部分

在這里插入圖片描述

在這里插入圖片描述

  1. 顯示部分一共4個界面,定義一個變量界面顯示模式變量Seg_Disp_Mode各個界面。
    后續通過對 Seg_Disp_Mode (界面顯示模式變量)的判斷切換不同的界面。
unsigned char Seg_Disp_Mode;           //界面顯示模式變量 0-頻率界面 1-參數界面 2-時間界面 3-回顯界面
	switch(Seg_Disp_Mode){case 0://頻率界面break;case 1://參數界面break;case 2://時間界面break;case 3://回顯界面break;}
  1. 在顯示功能部分中,需要顯示的字符有:0-9、熄滅、A、F、H、P、-,在seg.c文件段選數組中添加對應的十六進制表示。
code unsigned char Seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x88,0x8e,0x89,0x8c,0xBF};//0-9,10-熄滅,11-A,12-F,13-H,14-P,15--

頻率界面

在這里插入圖片描述

頻率界面由三部分組成:

  • 第一部分為編號(Seg_Buf[0]),在該界面下全程顯示字母F。
  • 第二部分熄滅部分(Seg_Buf[1] - Seg_Buf[2]),在該界面下全程熄滅。
  • 第三部分為頻率顯示部分(Seg_Buf[3] - Seg_Buf[7]),用NE555實時計算頻率。
正常顯示

數碼管顯示數組Seg_Buf[])對應的數碼管中輸入要顯示的字符在seg.c文件段選數組Seg_dula[])對應的十六進制表示的位置。

			Seg_Buf[0] = 12;//FSeg_Buf[1] = Seg_Buf[2] = 10;Seg_Buf[3] = Freq / 10000 % 10;Seg_Buf[4] = Freq / 1000 % 10;Seg_Buf[5] = Freq / 100 % 10;Seg_Buf[6] = Freq / 10 % 10;Seg_Buf[7] = Freq % 10;
高位熄滅

在這里插入圖片描述
while循環高位開始檢測,當高位是0時,將該位置的數碼管置為熄滅狀態。

注意:
顯示最小為0,即數碼管的最后一位(Seg_Buf[7])不論是否為0都要保證正常顯示,所以在while循環中不對最后一位檢測。

	unsigned char i=3;/*高位為0時熄滅*/while(Seg_Buf[i] == 0){Seg_Buf[i] = 10;if(++i == 7) break;}break;

參數界面

在這里插入圖片描述
參數界面中又有兩個子界面,定義參數界面模式變量Seg_DM1_Mode對兩個子界面標記。

bit Seg_DM1_Mode = 1;                      //參數界面模式變量 0-超限參數界面 1-校準值參數界面

定義參數數組Seg_Set_Arr,存儲兩個界面下的參數值。
介于校準值界面的參數值有負數的情況,可以將參數數組(Seg_Set_Arr)直接定義為有符號的數組,后續只要對校準值變量的正負進行判斷就可以確定校準值的符號位要不要點亮。

int Seg_Set_Arr[2] = {2000,0};         //參數數組 [0]-超限參數 [1]-校準值變量
基礎寫法:兩個界面分開來寫
  1. if 判斷當前子界面:Seg_DM1_Mode(參數界面模式變量)為0,處于超限參數界面;反之,處于校準值參數界面。
  2. 超限參數界面(邏輯和頻率界面的正常顯示部分相同,不再贅述)
  3. 校準值參數界面時:
    用if檢測校準值,分為校準值參數不為0校準值參數為0兩種情況。
    校準值參數不為0時:用一組 if 判斷校準值的正負性,先確定符號位,再對數值進行顯示。
    注意:校準值是有符號的數據,要取出各位數可以對他先取它的絕對值(math.h文件中的abs函數
    當校準值參數為0時:顯示一位0。
#include "math.h"  //abs函數
 		    if(Seg_DM1_Mode == 0)//超限參數界面{Seg_Buf[4] = Seg_Set_Arr[0] /1000 % 10;Seg_Buf[5] = Seg_Set_Arr[0] /100 % 10;Seg_Buf[6] = Seg_Set_Arr[0] /10 % 10;Seg_Buf[7] = Seg_Set_Arr[0] % 10;}else//校準值參數界面{if(Seg_Set_Arr[1] != 0){/*確定符號位*/if(Seg_Set_Arr[1] < 0)Seg_Buf[4] = 15;//-else Seg_Buf[4] = 10;//熄滅Seg_Buf[5] = abs(Seg_Set_Arr[1]) /100 % 10;Seg_Buf[6] = abs(Seg_Set_Arr[1]) /10 % 10;Seg_Buf[7] = abs(Seg_Set_Arr[1]) % 10;}else{Seg_Buf[5] = Seg_Buf[6] = 10;Seg_Buf[7] = 0;}				}
優化寫法:兩個界面合一起寫

(這個部分是我對本套題解析視頻做的筆記,西風老師這個寫法的思路真的蠻好的)

先看到具體代碼:

		Seg_Buf[0] = 14;//PSeg_Buf[1] = (unsigned char)Seg_DM1_Mode+1;Seg_Buf[2] = Seg_Buf[3] = 10;Seg_Buf[4] = Seg_DM1_Mode?(10 + 5 * (Seg_Set_Arr[Seg_DM1_Mode] < 0)):abs(Seg_Set_Arr[Seg_DM1_Mode]) /1000 % 10;Seg_Buf[5] = abs(Seg_Set_Arr[Seg_DM1_Mode]) /100 % 10;Seg_Buf[6] = abs(Seg_Set_Arr[Seg_DM1_Mode]) /10 % 10;Seg_Buf[7] = abs(Seg_Set_Arr[Seg_DM1_Mode]) % 10;/*校準值為0時,只顯示一位0*/if(Seg_Set_Arr[Seg_DM1_Mode] == 0)Seg_Buf[5] = Seg_Buf[6] = 10;

優化寫法重點在于:

  1. Seg_DM1_Mode(參數界面模式變量)和顯示相關變量的代數關系
  2. 條件運算符

簡單回顧一下條件運算符:
<表達式1>?<表達式2>:<表達式3>
當表達式1為真,返回表達式2的值;如果為假,則返回表達式3的值。

基礎的顯示不再贅述,在此對 Seg_Buf[1]Seg_Buf[4] 做簡單的分析:

  1. Seg_Buf[1]:
    由題目所給信息可知,Seg_Buf[1]在1和2之間變化,對應的是兩個子界面
    在此前我們已經給這兩個界面定義好了標志變量Seg_DM1_Mode(參數界面模式變量),它在0和1之間變化
    不難得出,Seg_Buf[1]和Seg_DM1_Mode之間的代數關系,即Seg_Buf[1]的值比Seg_DM1_Mode大1
    注意: Seg_DM1_Mode是bit型數據,不能到達2,所以讓Seg_DM1_Mode直接+1不能達到效果。那么需要解決的問題是增大它數值的可變區間,可以用強制類型轉換。
  2. Seg_Buf[4]:
    Seg_Buf[4]對應了兩種顯示狀態,即超限參數界面下的高位校準值參數界面下的符號位
    條件運算符對Seg_DM1_Mode(參數界面模式變量)進行判斷,可以劃分出兩種狀態,即當Seg_DM1_Mode為真(Seg_DM1_Mode為1)時,是校準值參數界面;當Seg_DM1_Mode為假(Seg_DM1_Mode為0)時,是超限參數界面。
    那么,只要找出Seg_Buf[4]在兩種顯示狀態下的數值在段選數組(Seg.h文件下的Seg_dula[])中的位置就可以完成顯示。
    Seg_Buf[4]在超限參數界面下的高位狀態時(Seg_DM1_Mode為0):只要獲取超限參數(Seg_Set_Arr[0])的最高位就行。
    Seg_Buf[4]在校準值參數界面下的符號位狀態時(Seg_DM1_Mode為1):Seg_Buf[4]在Seg_Set_Arr[1](校準值參數)為正,Seg_Buf[4]不點亮;Seg_Set_Arr[1](校準值參數)為負,Seg_Buf[4]才點亮,顯示符號位。所以還要找熄滅對應的位置和負號對應的位置之間的關系
    這里我覺得比較妙的一點是用條件是否為真(Seg_Set_Arr[Seg_DM1_Mode] < 0)進行了熄滅和負號點亮之間的切換。)

時間界面

在這里插入圖片描述
在這里插入圖片描述
定義**時間參數數組ucRtc[]**用來存儲時分秒的數據。

unsigned char ucRtc[3] = {13,03,05};   //時間參數數組 [0]-時 [1]-分 [2]-秒

調用ds1302里寫入的函數Set_Rtc(),將初始顯示狀態的數據在上電時寫入

	Set_Rtc(ucRtc);

每次進入信息處理函數時讀取(Read_Rtc())時鐘信息更新時間參數數組ucRtc[] 的數據,為后續顯示做準備。

	Read_Rtc(ucRtc);

(顯示可以一位數碼管一位數碼管寫,和頻率界面的正常顯示相似,就不再贅述了。)
for循環 將數碼管顯示的時分秒數據寫入。
這里要找出時分秒在數碼管的位置之間的代數關系,時分秒的高位分別占0 3 6,低位占1 4 7,可知高位滿足 y = 3x ,低位滿足 y = 3x+1。
在一次循環里實現一組高低位的取值。

			for(j = 0;j < 3;j++){Seg_Buf[j * 3] = ucRtc[j] / 10;Seg_Buf[j * 3 + 1] = ucRtc[j] %10;}Seg_Buf[2] = Seg_Buf[5] = 15;//-

回顯界面

在這里插入圖片描述
回顯界面也有兩個子界面,聲明回顯界面模式變量bit Seg_DM3_Mode標記兩個子界面。
頻率回顯界面要顯示最大頻率值,時間回顯界面要顯示最大頻率發生時間,分別聲明最大頻率值變量Max_Freq和**最大頻率發生時間數組Max_Freq_Time[3]**存儲這兩個值。

bit Seg_DM3_Mode;                      //回顯界面模式變量 0-頻率回顯界面 1-時間回顯界面
unsigned int Max_Freq;                 //最大頻率值變量
unsigned int Max_Freq_Time[3];          //最大頻率發生時間數組

每次進入信息處理函數后獲取最大頻率及其發生時間,存到對應的變量里,以供后續調用。

/*獲取最大頻率及其發生時間*/if(Max_Freq < Freq){Max_Freq = Freq;for(j = 0;j < 3;j ++)Max_Freq_Time[j] = ucRtc[j];}

頻率回顯界面要注意高位熄滅
時間回顯界面用循環獲取時間的話,和時間界面相似,要找出各位之間的代數關系。

			Seg_Buf[0] = 13;//Hif(Seg_DM3_Mode == 0)//頻率回顯界面{Seg_Buf[1] = 12;//FSeg_Buf[2] = 10;Seg_Buf[3] = Max_Freq / 10000 % 10;Seg_Buf[4] = Max_Freq / 1000 % 10;Seg_Buf[5] = Max_Freq / 100 % 10;Seg_Buf[6] = Max_Freq / 10 % 10;Seg_Buf[7] = Max_Freq % 10;/*高位為0時熄滅*/while(Seg_Buf[i] == 0){Seg_Buf[i] = 10;if(++i == 7) break;}}else//時間回顯界面{Seg_Buf[1] = 11;//Afor(j = 0;j < 3;j ++){Seg_Buf[2+2*j] =  Max_Freq_Time[j] / 10;Seg_Buf[2+2*j+1] =  Max_Freq_Time[j] % 10;}}

校準

在這里插入圖片描述

校準過程

顯示的頻率都是實時頻率加上校準值后的結果,那么只要在計算完頻率之后加上校準值即可。

Freq += Seg_Set_Arr[1];  //加上校準值
校準錯誤顯示

校準錯誤顯示在頻率顯示界面(Seg_Disp_Mode為0)下實現
可以將顯示分為兩種情況

  1. 頻率正常
  2. 頻率錯誤

if 對校準后的頻率進行判斷,當頻率大于0時,正常讀取正常顯示;當頻率小于0時,只顯示首位的編號和末兩位的LL。

		if(Freq >= 0){Seg_Buf[3] = Freq / 10000 % 10;Seg_Buf[4] = Freq / 1000 % 10;Seg_Buf[5] = Freq / 100 % 10;Seg_Buf[6] = Freq / 10 % 10;Seg_Buf[7] = Freq % 10;/*高位為0時熄滅*/while(Seg_Buf[i] == 0){Seg_Buf[i] = 10;if(++i == 7) break;}}else{Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] = 10;//熄滅Seg_Buf[6] = Seg_Buf[7] = 16;//LL} 

注意: Freq要用 long int型
考慮兩個點 :

  1. 頻率的正負。因為加上校驗值后頻率可能為負數,如果用無符號型無論如何都得不到負數,所以要用有符號型的數據。
  2. 范圍。int型的取值范圍為 -32768 到 32767 ,用來存儲單純的頻率是夠用的,但是加上校驗值后就可能產生數據溢出的問題,使得顯示數據不準,所以要擴大取值范圍

DAC輸出部分

在這里插入圖片描述
在這里插入圖片描述
聲明輸出電壓變量V_OutPut,存儲要輸出的電壓大小。

float V_OutPut;                         //輸出電壓變量

整體的輸出可以把分成四段:

  1. 頻率錯誤(Frqe<0)時,DAC輸出0V。
  2. 頻率大于等于0且小于500Hz時,DAC輸出1V
  3. 頻率大于超限參數時,DAC輸出5V
  4. 頻率在500Hz和超過限制頻率之間時,DAC輸出要求出該斜線的表達式。
    易得數學表達式:
    V = ( ( 5 - 1 ) / ( 超限參數 - 500 ) ) * (F-500) + 1
  5. 頻率錯誤時,DAC輸出0V。

注意: V_OutPut(輸出電壓變量)寫入的 0-5 的值是需要輸出的模擬輸出值,而在調用DAC輸出函數(Da_Write)時寫入的參數是 0 - 255 范圍內的數字輸入值。如果直接用V_OutPut的值寫入參數,那么實際輸出的電壓值將會很小,所以在此要將 0-5 范圍的值線性映射成 0-255 范圍的量值,不難得出這兩個范圍的倍數關系是51,因此只要對小的值(輸出電壓變量V_OutPut)乘上51即可完成映射。

/*DAC相關*/if(Freq < 0) V_OutPut = 0;else if(Freq >= 0 && Freq <= 500) V_OutPut = 1;else if(Freq >= Seg_Set_Arr[0]) V_OutPut = 5;else V_OutPut = 1 + (float)(5.0 - 1.0)/(Seg_Set_Arr[0] - 500) * (Freq - 500);Da_Write(V_OutPut * 51);

按鍵功能部分

在這里插入圖片描述
在這里插入圖片描述

/*按鍵處理函數*/
void Key_Proc()
{if(Key_Flag) return;Key_Flag = 1;Key_Val = Key_Read();Key_Down = Key_Val & (Key_Val ^ Key_Old);Key_Old = Key_Val;switch(Key_Down){case 4://“界面”按鍵break;case 5://“選擇”按鍵break;case 8://“加”按鍵break;case 9://“減”按鍵break;}
}
S4:“界面”按鍵

在這里插入圖片描述

界面切換要考慮模式變量的取值情況
按鍵按下一次模式切換一次,即 Seg_Disp_Mode(界面顯示模式變量)+1。Seg_Disp_Mode在 0-3 之間變化,對應4個模式,最大值不能為3,所以當 Seg_Disp_Mode 加到4時,要將它重新賦0,以此形成一個循環的切換

if(++Seg_Disp_Mode == 4) Seg_Disp_Mode = 0;
S5:“選擇”按鍵

在這里插入圖片描述
分成兩種情況對各自界面下的模式變量操作。
因為這兩個界面下的模式變量都是bit型,可以不用像“界面”按鍵那里一樣進行加法操作,可以用異或。
這里用異或,其實就相當于取反。因為bit型數據本身就只有0和1兩個數值,0異或后為1,1異或后為0。

			if(Seg_Disp_Mode == 1)Seg_DM1_Mode ^= 1;else if(Seg_Disp_Mode == 3)Seg_DM3_Mode ^= 1;

注意:

  1. 頻率界面切換到參數界面和時間界面切換到回顯界面時,有默認顯示的子界面,所以在每次在完成這兩種切換時,都要保證當前顯示處于默認子界面,但是在上面的操作中并不能實現。
  2. 默認子界面的顯示是在按下“界面”按鍵時生效的(Key_Down為4),即要對“界面”按鍵按下后將要顯示的界面進行判斷,決定是否要重置子界面的模式變量
            if(++Seg_Disp_Mode == 1)  Seg_DM1_Mode = 0;   //默認顯示超限參數子界面if(Seg_Disp_Mode == 3)  Seg_DM3_Mode = 0;     //默認顯示頻率回顯子界面if(Seg_Disp_Mode == 4)  Seg_Disp_Mode = 0;    //退回到最初界面
優化寫法

上面是對按鍵按下后的界面進行了判斷后才執行重置。但是,其實不進行判斷也是可以的。因為子界面模式變量的值是多少對于另外兩個界面的顯示沒有任何影響,所以可以省去判斷的那一步,在每次按下按鍵就重置。

			if(++Seg_Disp_Mode == 4) Seg_DM1_Mode = Seg_DM3_Mode = Seg_Disp_Mode = 0;
S8、S9:“加”按鍵 、“減”按鍵

在這里插入圖片描述

在這里插入圖片描述
“加”按鍵和“減”按鍵邏輯類似,此處只對“加”按鍵做簡單的解析。
在進行加法運算時,要考慮單次加的單位值大小參數的上限值
此處是對同界面下的兩個子界面的參數進行的加法,所以可以將兩個子界面的單次加的單位值和參數上限值分組后分別存在兩個數組中(單次加減大小數組Step_Arr[2]上限值數組Max_Arr[2]),前一個數是超限參數對應的值,后一個數是校準參數對應的值。

//按鍵功能相關變量
int code Step_Arr[2] = {1000,100};     //單次加減大小數組 [0]-超限參數單次增加量/減小量 [1]-校準參數單次增加量/減小量
int code Max_Arr[2] = {9000,900};      //上限值數組 [0]-超限參數上限值 [1]-校準參數上限值
int code Min_Arr[2] = {1000,-900};     //下限值數組 [0]-超限參數下限值 [1]-校準參數下限值

通過參數界面模式變量(Seg_DM1_Mode) 根據當前界面的不同獲取對應的值,進行加法處理。
每次加完都要對當前數值進行判斷,避免超出限制
(減法運算邏輯類似,不贅述)

			case 8://“加”按鍵if(Seg_Disp_Mode == 1 ){Seg_Set_Arr[Seg_DM1_Mode] += Step_Arr[Seg_DM1_Mode];if(Seg_Set_Arr[Seg_DM1_Mode] > Max_Arr[Seg_DM1_Mode])Seg_Set_Arr[Seg_DM1_Mode] = Max_Arr[Seg_DM1_Mode];}break;case 9://“減”按鍵if(Seg_Disp_Mode == 1 ){Seg_Set_Arr[Seg_DM1_Mode] -= Step_Arr[Seg_DM1_Mode];if(Seg_Set_Arr[Seg_DM1_Mode] < Min_Arr[Seg_DM1_Mode])Seg_Set_Arr[Seg_DM1_Mode] = Min_Arr[Seg_DM1_Mode];}break;

LED指示燈功能部分

在這里插入圖片描述
這塊的重點是兩個地方:

  1. 0.2s間隔的閃爍。
  2. 亮、滅狀態的切換。

聲明閃爍間隔計時變量Time_200ms,后續用于0.2間隔閃爍的計時。
聲明閃爍標志位Led_Flag,用于標記亮滅狀態,并且通過它的改變實現亮滅的切換。

//LED指示燈功能相關變量
unsigned char Time_200ms;              //閃爍間隔計時變量
bit Led_Flag;                          //閃爍標志位

在定時中斷服務函數中完成。
每計時滿200ms重新開始計時,并且改變一次亮滅狀態。

/*LED閃爍200ms計時*/if(++Time_200ms == 200){Time_200ms = 0;Led_Flag ^= 1;}

(在實現點亮時,可以用最基礎的 if 的方式一步一步判斷,這個邏輯很簡單,暫時先不寫了。下面這個是西風老師的邏輯,我認為比最基礎版寫法更好,因為更能體現你對C語言運算符運用,對此我做個簡要的分析一下)
題目涉及的LED只有L1和L2,所以只要對Led控制數組(ucLed[])前兩位進行操作。

  1. L1在頻率界面下生效(Seg_Disp_Mode為0),用邏輯非運算符( !) 對 Seg_Disp_Mode 的邏輯值取反。即Seg_Disp_Mode為 0時(當前處于頻率界面),那邏輯值就是假,! 運算之后的結果就是 1;Seg_Disp_Mode若不為 0(當前不處于頻率界面),那邏輯值就是真,! 運算之后的結果就是 0。
    再和閃爍標志位(Led_Flag)進行邏輯與(有0出0)的運算,那么當Seg_Disp_Mode為 0時(當前處于頻率界面),L1的點亮狀態全由 Led_Flag(閃爍標志位)決定
  2. L2在頻率大于超限參數 或者 頻率為負數時生效,因為兩種情況中任意滿足一種它就生效,所以可以對這兩個情況進行邏輯或運算(其中一個操作數的邏輯值為真,整個表達式的結果就為真)。
    處于頻率大于超限參數時,在這可以直接對頻率判斷 也 可以對DAC輸出電壓變量(V_OutPut)進行判斷。對 V_OutPut 判斷(回顧DAC輸出部分),當 V_OutPut 為 5 時表示當前處于頻率大于超限參數的狀態,要實現L2閃爍。那么只要對 V_OutPut == 5 進行真假邏輯的判斷就可以用1和0來表示 頻率大于超限參數(L2閃爍)頻率不大于超限參數(L2不閃爍) 兩種情況。此時就和L1的思路相似,將 V_OutPut == 5 邏輯真假的輸出值和閃爍標志位(Led_Flag)進行邏輯與(有0出0)的運算,那么當邏輯為真(頻率大于超限參數(L2閃爍))時,L2 的點亮狀態全由 Led_Flag(閃爍標志位)決定。
    處于頻率為負數時,直接對Freq < 0進行邏輯判斷,那么當頻率為負數是輸出為1,此時L1點亮。
	/**LED相關**/ucLed[0] = Led_Flag & (!Seg_Disp_Mode);ucLed[1] = (Led_Flag & (V_OutPut == 5)) || (Freq < 0);

調試時發現的問題

最終代碼

User文件

main.c

/*頭文件聲明區*/
#include <STC15F2K60S2.H>
#include "Init.h"
#include "Seg.h"
#include "Key.h"
#include "Led.h"
#include "iic.h"
#include "ds1302.h"
#include "math.h"/*變量聲明區*/
unsigned char Key_Down,Key_Val,Key_Up,Key_Old;
unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};
unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};
unsigned char Seg_Pos;
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};
unsigned int Slow_Down;
bit Seg_Flag,Key_Flag;
unsigned int Time_1s;
long int Freq;//界面相關變量
unsigned char Seg_Disp_Mode;           //界面顯示模式變量 0-頻率界面 1-參數界面 2-時間界面 3-回顯界面
bit Seg_DM1_Mode;                      //參數界面模式變量 0-超限參數界面 1-校準值參數界面
int Seg_Set_Arr[2] = {2000,0};         //參數數組 [0]-超限參數 [1]-校準值變量
unsigned char ucRtc[3] = {13,03,05};   //時間參數數組 [0]-時 [1]-分 [2]-秒
bit Seg_DM3_Mode;                      //回顯界面模式變量 0-頻率回顯界面 1-時間回顯界面
unsigned int Max_Freq;                 //最大頻率值變量
unsigned int Max_Freq_Time[3];         //最大頻率發生時間數組//DAC相關變量
float V_OutPut;                        //輸出電壓變量//按鍵功能相關變量
int code Step_Arr[2] = {1000,100};     //單次加減大小數組 [0]-超限參數單次增加量/減小量 [1]-校準參數單次增加量/減小量
int code Max_Arr[2] = {9000,900};      //上限值數組 [0]-超限參數上限值 [1]-校準參數上限值
int code Min_Arr[2] = {1000,-900};     //下限值數組 [0]-超限參數下限值 [1]-校準參數下限值//LED指示燈功能相關變量
unsigned char Time_200ms;              //閃爍間隔計時變量
bit Led_Flag;                          //閃爍標志位/*按鍵處理函數*/
void Key_Proc()
{if(Key_Flag) return;Key_Flag = 1;Key_Val = Key_Read();Key_Down = Key_Val & (Key_Val ^ Key_Old);Key_Old = Key_Val;switch(Key_Down){case 4://“界面”按鍵
//			 /*分開寫*/
//			if(++Seg_Disp_Mode == 1)  Seg_DM1_Mode = 0;   //默認顯示超限參數子界面
//			if(Seg_Disp_Mode == 3)  Seg_DM3_Mode = 0;     //默認顯示頻率回顯子界面
//			if(Seg_Disp_Mode == 4)  Seg_Disp_Mode = 0;    //退回到最初界面/*合在一起寫*/if(++Seg_Disp_Mode == 4) Seg_DM1_Mode = Seg_DM3_Mode = Seg_Disp_Mode = 0;break;case 5://“選擇”按鍵if(Seg_Disp_Mode == 1)Seg_DM1_Mode ^= 1;else if(Seg_Disp_Mode == 3)Seg_DM3_Mode ^= 1;break;case 8://“加”按鍵if(Seg_Disp_Mode == 1 ){Seg_Set_Arr[Seg_DM1_Mode] += Step_Arr[Seg_DM1_Mode];if(Seg_Set_Arr[Seg_DM1_Mode] >= Max_Arr[Seg_DM1_Mode])Seg_Set_Arr[Seg_DM1_Mode] = Max_Arr[Seg_DM1_Mode];}break;case 9://“減”按鍵if(Seg_Disp_Mode == 1 ){Seg_Set_Arr[Seg_DM1_Mode] -= Step_Arr[Seg_DM1_Mode];if(Seg_Set_Arr[Seg_DM1_Mode] < Min_Arr[Seg_DM1_Mode])Seg_Set_Arr[Seg_DM1_Mode] = Min_Arr[Seg_DM1_Mode];}break;}
}/*信息處理函數*/
void Seg_Proc()
{unsigned char i = 3;unsigned char j = 0;if(Seg_Flag) return;Seg_Flag = 1;/*更新時鐘*/Read_Rtc(ucRtc);/*獲取最大頻率及其發生時間*/if(Max_Freq < Freq){Max_Freq = Freq;for(j = 0;j < 3;j ++)Max_Freq_Time[j] = ucRtc[j];}switch(Seg_Disp_Mode){case 0://頻率界面Seg_Buf[0] = 12;//FSeg_Buf[1] = Seg_Buf[2] = 10;if(Freq >= 0){Seg_Buf[3] = Freq / 10000 % 10;Seg_Buf[4] = Freq / 1000 % 10;Seg_Buf[5] = Freq / 100 % 10;Seg_Buf[6] = Freq / 10 % 10;Seg_Buf[7] = Freq % 10;/*高位為0時熄滅*/while(Seg_Buf[i] == 0){Seg_Buf[i] = 10;if(++i == 7) break;}}else{Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] = 10;//熄滅Seg_Buf[6] = Seg_Buf[7] = 16;//LL} break;case 1://參數界面Seg_Buf[0] = 14;//PSeg_Buf[1] = (unsigned char)Seg_DM1_Mode+1;Seg_Buf[2] = Seg_Buf[3] = 10;//		  if(Seg_DM1_Mode == 0)//超限參數界面
//			{
//				Seg_Buf[4] = Seg_Set_Arr[0] /1000 % 10;
//				Seg_Buf[5] = Seg_Set_Arr[0] /100 % 10;
//				Seg_Buf[6] = Seg_Set_Arr[0] /10 % 10;
//				Seg_Buf[7] = Seg_Set_Arr[0] % 10;
//			}
//			else//校準值參數界面
//			{
//				if(Seg_Set_Arr[1] != 0)
//				{
//					/*確定符號位*/
//					if(Seg_Set_Arr[1] < 0)
//						Seg_Buf[4] = 15;//-
//					else 
//						Seg_Buf[4] = 10;//熄滅
//					
//					
//					Seg_Buf[5] = abs(Seg_Set_Arr[1]) /100 % 10;
//				  Seg_Buf[6] = abs(Seg_Set_Arr[1]) /10 % 10;
//				  Seg_Buf[7] = abs(Seg_Set_Arr[1]) % 10;
//				}
//				else
//				{
//					Seg_Buf[5] = Seg_Buf[6] = 10;
//				  Seg_Buf[7] = 0;
//				}				
//			}Seg_Buf[4] = Seg_DM1_Mode?(10 + 5 * (Seg_Set_Arr[Seg_DM1_Mode] < 0)):abs(Seg_Set_Arr[Seg_DM1_Mode]) /1000 % 10;Seg_Buf[5] = abs(Seg_Set_Arr[Seg_DM1_Mode]) /100 % 10;Seg_Buf[6] = abs(Seg_Set_Arr[Seg_DM1_Mode]) /10 % 10;Seg_Buf[7] = abs(Seg_Set_Arr[Seg_DM1_Mode]) % 10;/*校準值為0時,只顯示一位0*/if(Seg_Set_Arr[Seg_DM1_Mode] == 0)Seg_Buf[5] = Seg_Buf[6] = 10;break;case 2://時間界面for(j = 0;j < 3;j++){Seg_Buf[j * 3] = ucRtc[j] / 10;Seg_Buf[j * 3 + 1] = ucRtc[j] %10;}Seg_Buf[2] = Seg_Buf[5] = 15;//-break;case 3://回顯界面Seg_Buf[0] = 13;//Hif(Seg_DM3_Mode == 0)//頻率回顯界面{Seg_Buf[1] = 12;//FSeg_Buf[2] = 10;Seg_Buf[3] = Max_Freq / 10000 % 10;Seg_Buf[4] = Max_Freq / 1000 % 10;Seg_Buf[5] = Max_Freq / 100 % 10;Seg_Buf[6] = Max_Freq / 10 % 10;Seg_Buf[7] = Max_Freq % 10;/*高位為0時熄滅*/while(Seg_Buf[i] == 0){Seg_Buf[i] = 10;if(++i == 7) break;}}else//時間回顯界面{Seg_Buf[1] = 11;//Afor(j = 0;j < 3;j ++){Seg_Buf[2+2*j] =  Max_Freq_Time[j] / 10;Seg_Buf[2+2*j+1] =  Max_Freq_Time[j] % 10;}}break;}
}/*其他顯示函數*/
void Led_Proc()
{/*DAC相關*/if(Freq < 0) V_OutPut = 0;else if(Freq >= 0 && Freq <= 500) V_OutPut = 1;else if(Freq >= Seg_Set_Arr[0]) V_OutPut = 5;else V_OutPut = 1 + (float)(5.0 - 1.0)/(Seg_Set_Arr[0] - 500) * (Freq - 500);Da_Write(V_OutPut * 51);/**LED相關**/ucLed[0] = Led_Flag & (!Seg_Disp_Mode);ucLed[1] = (Led_Flag & (V_OutPut == 5)) || (Freq < 0);
}/*定時器0初始化函數*/
void Timer0Init(void)		//1毫秒@12.000MHz
{AUXR &= 0x7F;		//定時器時鐘12T模式TMOD &= 0xF0;		//設置定時器模式TMOD |= 0x05;		TL0 = 0;		//設置定時初值TH0 = 0;		//設置定時初值TF0 = 0;		//清除TF0標志TR0 = 1;		//定時器0開始計時
}/*定時器1初始化函數*/
void Timer1Init(void)		//1毫秒@12.000MHz
{AUXR &= 0xBF;		//定時器時鐘12T模式TMOD &= 0x0F;		//設置定時器模式TL1 = 0x18;		//設置定時初值TH1 = 0xFC;		//設置定時初值TF1 = 0;		//清除TF1標志TR1 = 1;		//定時器1開始計時ET1 = 1;EA = 1;
}/*定時器1中斷服務函數*/
void Timer1_Isr(void) interrupt 3
{/*數碼管90ms的減速*/if(++Slow_Down == 90){Seg_Flag = Slow_Down = 0;}/*按鍵10ms的減速*/if(Slow_Down % 10 == 0){Key_Flag = 0;}/*1s的計數*/if(++Time_1s == 1000){Time_1s = 0;Freq = TH0 << 8 | TL0;   //計算頻率Freq += Seg_Set_Arr[1];  //加上校準值TH0 = 0;TL0 = 0;}/*信息處理*/Seg_Disp(Slow_Down % 8, Seg_Buf[Slow_Down % 8],Seg_Point[Slow_Down % 8]);Led_Disp(Slow_Down % 8,ucLed[Slow_Down % 8]);/*LED閃爍200ms計時*/if(++Time_200ms == 200){Time_200ms = 0;Led_Flag ^= 1;}
}/*主函數*/
void main()
{Sys_Init();Timer0Init();Timer1Init();Set_Rtc(ucRtc);while(1){Key_Proc();Seg_Proc();Led_Proc();}
}

Driver文件

init.c

#include "Init.h"void Sys_Init()
{P0 = 0xff;P2 = P2 & 0x1f | 0x80;P2 &= 0x1f;P0 = 0x00;P2 = P2 & 0x1f | 0xa0;P2 &= 0x1f;
}

Key.c

#include "Key.h"unsigned char Key_Read()
{unsigned char temp=0;P44 = 0; P42 = 1; P35 = 1; P34 = 1;if(P33 == 0) temp = 4;if(P32 == 0) temp = 5;if(P31 == 0) temp = 6;if(P30 == 0) temp = 7;P44 = 1; P42 = 0; P35 = 1; P34 = 1;if(P33 == 0) temp = 8;if(P32 == 0) temp = 9;if(P31 == 0) temp = 10;if(P30 == 0) temp = 11;P44 = 1; P42 = 1; P35 = 0; P34 = 1;if(P33 == 0) temp = 12;if(P32 == 0) temp = 13;if(P31 == 0) temp = 14;if(P30 == 0) temp = 15;/*P34引腳和NE555沖突*/
//	P44 = 1; P42 = 1; P35 = 1; P34 = 0;
//	if(P33 == 0) temp = 16;
//	if(P32 == 0) temp = 17;
//	if(P31 == 0) temp = 18;
//	if(P30 == 0) temp = 19;return temp;
}

Led.c

#include "Led.h"void Led_Disp(unsigned char addr,enable)
{unsigned char temp = 0x00;unsigned char temp_old = 0xff;if(enable)temp |= 0x01 << addr;elsetemp &= ~(0x01 << addr);if(temp != temp_old){P0 = ~temp;P2 = P2 & 0x1f | 0x80;P2 &= 0x1f;temp_old = temp;}
}

Seg.c

#include "Seg.h"code unsigned char Seg_wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
code unsigned char Seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x88,0x8e,0x89,0x8c,0xBF,0xC7};//0-9,10-熄滅,11-A,12-F,13-H,14-P,15--,16-Lvoid Seg_Disp(unsigned char wela,dula,point)
{P0 = 0xff;P2 = P2 & 0x1f | 0xe0;P2 &= 0x1f;P0 = Seg_wela[wela];P2 = P2 & 0x1f | 0xc0;P2 &= 0x1f;P0 = Seg_dula[dula];if(point)P0 &= 0x7f;P2 = P2 & 0x1f | 0xe0;P2 &= 0x1f;
}

iic.c

#include "iic.h"
#include "intrins.h"
#define DELAY_TIME	10sbit scl = P2 ^ 0;
sbit sda = P2 ^ 1;//
static void I2C_Delay(unsigned char n)
{do{_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();		}while(n--);      	
}//
void I2CStart(void)
{sda = 1;scl = 1;I2C_Delay(DELAY_TIME);sda = 0;I2C_Delay(DELAY_TIME);scl = 0;    
}//
void I2CStop(void)
{sda = 0;scl = 1;I2C_Delay(DELAY_TIME);sda = 1;I2C_Delay(DELAY_TIME);
}//
void I2CSendByte(unsigned char byt)
{unsigned char i;for(i=0; i<8; i++){scl = 0;I2C_Delay(DELAY_TIME);if(byt & 0x80){sda = 1;}else{sda = 0;}I2C_Delay(DELAY_TIME);scl = 1;byt <<= 1;I2C_Delay(DELAY_TIME);}scl = 0;  
}//
unsigned char I2CReceiveByte(void)
{unsigned char da;unsigned char i;for(i=0;i<8;i++){   scl = 1;I2C_Delay(DELAY_TIME);da <<= 1;if(sda) da |= 0x01;scl = 0;I2C_Delay(DELAY_TIME);}return da;    
}//
unsigned char I2CWaitAck(void)
{unsigned char ackbit;scl = 1;I2C_Delay(DELAY_TIME);ackbit = sda; scl = 0;I2C_Delay(DELAY_TIME);return ackbit;
}//
void I2CSendAck(unsigned char ackbit)
{scl = 0;sda = ackbit; I2C_Delay(DELAY_TIME);scl = 1;I2C_Delay(DELAY_TIME);scl = 0; sda = 1;I2C_Delay(DELAY_TIME);
}void Da_Write(unsigned char dat)
{I2CStart();I2CSendByte(0x90);I2CWaitAck();I2CSendByte(0x41);I2CWaitAck();I2CSendByte(dat);I2CWaitAck();I2CStop();
}

ds1302.c

#include "ds1302.h"
#include "intrins.h"sbit SCK = P1 ^ 7;
sbit SDA = P2 ^ 3;
sbit RST = P1 ^ 3;
//
void Write_Ds1302(unsigned  char temp) 
{unsigned char i;for (i=0;i<8;i++)     	{ SCK = 0;SDA = temp&0x01;temp>>=1; SCK=1;}
}   //
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{RST=0;	_nop_();SCK=0;	_nop_();RST=1; 	_nop_();  Write_Ds1302(address);	Write_Ds1302(dat);		RST=0; 
}//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{unsigned char i,temp=0x00;RST=0;	_nop_();SCK=0;	_nop_();RST=1;	_nop_();Write_Ds1302(address);for (i=0;i<8;i++) 	{		SCK=0;temp>>=1;	if(SDA)temp|=0x80;	SCK=1;} RST=0;	_nop_();SCK=0;	_nop_();SCK=1;	_nop_();SDA=0;	_nop_();SDA=1;	_nop_();return (temp);			
}void Set_Rtc(unsigned char *ucRtc)
{unsigned char i;Write_Ds1302_Byte(0x8e,0x00);for(i = 0;i < 3; i++)Write_Ds1302_Byte(0x84 - 2 * i, ucRtc[i] / 10 % 10 << 4 | ucRtc[i] % 10);Write_Ds1302_Byte(0x8e,0x80);
}void Read_Rtc(unsigned char *ucRtc)
{unsigned char i;unsigned temp;for(i = 0; i < 3; i++){temp = Read_Ds1302_Byte(0x85 - 2 * i);ucRtc[i] = temp /16 * 10 + temp % 16;}
}

結語

這套題整理也花了不少時間,但在整理的同時也讓我對這套題的解法和思路有了更清晰的認知,收獲還是蠻大的。
感謝你能看到這里,希望這篇筆記不僅對我有所幫助,也能對你能有所啟發。
那么,文章里面有問題的話,也感謝大家指出。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/897702.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897702.shtml
英文地址,請注明出處:http://en.pswp.cn/news/897702.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue3實戰學習(Vue3快速搭建后臺管理系統(網頁頭部、側邊導航欄、主體數據展示區的設計與實現)(超詳細))(9)

目錄 一、Vue3工程環境配置、項目基礎腳手架搭建、Vue3基礎語法、Vue3集成Element-Plus的詳細教程。(博客鏈接如下) 二、Vue3集成Element-Plus詳細教程。(博客鏈接如下) 三、Vue3集成Vue-Router詳細教程。(博客鏈接如下) 四、Vue3快速搭建后臺管理系統。(實戰學習) &#xff08…

halcon機器人視覺(四)calibrate_hand_eye_stationary_3d_sensor

目錄 一、準備數據和模型二、按照表面匹配的的結果進行手眼標定三、根據標定結果計算CalObjInCamPose一、準備數據和模型 1、讀3D模型:read_object_model_3d 2、創建表面匹配模板:create_surface_model 3、創建一個HALCON校準數據模型:create_calib_data read_object_mode…

【菜鳥飛】通過vsCode用python訪問deepseek-r1等模型

目標 通過vsCode用python訪問deepseek。 環境準備 沒有環境的&#xff0c;vscode環境準備請參考之前的文章&#xff0c;另外需安裝ollama&#xff1a; 【菜鳥飛】用vsCode搭建python運行環境-CSDN博客 AI入門1&#xff1a;AI模型管家婆ollama的安裝和使用-CSDN博客 選讀文章…

vue中,watch里,this為undefined的兩種解決辦法

提示&#xff1a;vue中&#xff0c;watch里&#xff0c;this為undefined的兩種解決辦法 文章目錄 [TOC](文章目錄) 前言一、問題二、方法1——使用function函數代替箭頭函數()>{}三、方法2——使用that總結 前言 ?????盡量使用方法1——使用function函數代替箭頭函數()…

【如何使用云服務器與API搭建專屬聊天系統:寶塔面板 + Openwebui 完整教程】

文章目錄 不挑電腦、不用技術&#xff0c;云服務器 API 輕松搭建專屬聊天系統&#xff0c;對接 200 模型&#xff0c;數據全在自己服務器&#xff0c;安全超高一、前置準備&#xff1a;3 分鐘快速上手指南云服務器準備相關賬號注冊 二、手把手部署教程&#xff08;含代碼塊&a…

使用 PresentMon 獲取屏幕幀率

PresentMon是一個用于捕獲和分析Windows上圖形應用程序高性能特性的工具集,最初由GameTechDev開發,現由英特爾維護和推廣。PresentMon能夠追蹤關鍵性能指標,如CPU、GPU和顯示器的幀持續時間和延遲等,并支持多種圖形API(如DirectX、OpenGL和Vulkan)以及不同的硬件配置和桌…

基金交易系統的流程

1. 賬戶管理 開戶&#xff1a;投資者在基金銷售機構&#xff08;如銀行、券商或第三方平臺&#xff09;開立基金賬戶和資金賬戶。賬戶驗證&#xff1a;驗證投資者身份&#xff0c;綁定銀行卡或其他支付方式。 2. 交易申請 投資者下單&#xff1a;通過交易終端&#xff08;APP…

vue2雙向綁定解析

Vue 2 的雙向綁定原理基于 Object.defineProperty&#xff0c;核心源碼在 src/core/observer 目錄中。 1. 核心模塊&#xff1a;observer observer 模塊負責將普通對象轉換為響應式對象&#xff0c;主要包括以下文件&#xff1a; index.js&#xff1a;定義 Observer 類&#…

中級軟件設計師2004-2024軟考真題合集下載

中級軟件設計師2004-2024軟考真題合集下載 &#x1f31f; 資源亮點&#x1f3af; 適用人群&#x1f4a1; 資源使用指南&#x1f4cc; 資源獲取方式 &#x1f31f; 資源亮點 「中級軟件設計師歷年真題及答案解析&#xff08;2004-2024&#xff09;」 是全網最全、最新的備考資料…

艾爾登復刻Ep1——客戶端制作、場景切換、網絡控制

需要添加的插件內容 Netcode for GameObjects&#xff1a;是一個為 Unity 游戲開發提供高級網絡功能的 SDK。它的主要作用是允許開發者在其 GameObject 和 MonoBehaviour 工作流中集成網絡功能&#xff0c;并且可以與多種底層傳輸層協議兼容。 具體內容請看&#xff1a;https:…

2025探索短劇行業新可能報告40+份匯總解讀|附PDF下載

原文鏈接&#xff1a;https://tecdat.cn/?p41043 近年來&#xff0c;短劇以其緊湊的劇情、碎片化的觀看體驗&#xff0c;迅速吸引了大量用戶。百度作為互聯網巨頭&#xff0c;在短劇領域積極布局。從早期建立行業專屬模型冷啟動&#xff0c;到如今構建完整的商業生態&#xf…

正常的一個編碼器的架構是怎么樣,有哪些模塊構成

正常的一個編碼器架構及其模塊構成 在音視頻編碼器&#xff08;Video Encoder&#xff09;中&#xff0c;通常包括多個核心模塊&#xff0c;整個編碼器架構遵循 輸入 -> 預處理 -> 編碼核心 -> 碼流封裝 的流程。 1. 編碼器的整體架構 編碼器的主要架構如下&#xf…

文件解析漏洞練習

iis6的目錄解析漏洞 (.asp目錄中的所有文件都會被當做asp文件執行) 1.在iis的網站根目錄新建一個名為x.asp的文件 2.在x.asp中新建一個jpg文件。內容為<%now()%> asp代碼。 3.在外部瀏覽器中訪問windows2003的iis網站中的2.jpg 發現asp代碼被執行 iis6的分號截斷解析漏洞…

SQL Server性能優化實戰:從瓶頸定位到高效調優

引言 在數據庫應用中,性能問題直接影響用戶體驗和系統穩定性。本文基于實際案例,分享SQL Server性能優化的關鍵步驟與實用技巧,涵蓋問題定位、索引優化、查詢調優等多個維度。 目錄 引言 一、性能瓶頸定位 1.1 監控工具使用 二、索引優化實戰 2.1 索引碎片整理 2.2 缺…

【DNS系列】使用TCP傳輸

DNS ?默認使用UDP協議?&#xff08;端口53&#xff09;進行通信&#xff0c;但在以下場景中會切換到TCP協議?&#xff08;端口53&#xff09;&#xff1a; ?1. 響應數據過大&#xff08;超過512字節&#xff09;? ?UDP限制&#xff1a;DNS的UDP協議默認限制單個數據包大…

Go Ebiten小游戲開發:俄羅斯方塊

在這篇文章中&#xff0c;我們將一起開發一個簡單的俄羅斯方塊游戲&#xff0c;使用Go語言和Ebiten游戲庫。Ebiten是一個輕量級的游戲庫&#xff0c;適合快速開發2D游戲。我們將逐步構建游戲的基本功能&#xff0c;包括游戲邏輯、圖形繪制和用戶輸入處理。 項目結構 我們的項…

MySQL中IN關鍵字與EXIST關鍵字的比較

文章目錄 **功能等價性分析****執行計劃分析**&#xff1a; **1. EXISTS 的工作原理****步驟拆解**&#xff1a; **2. 為什么需要“利用索引快速定位”&#xff1f;****索引作用示例**&#xff1a; **3. 與 IN 子查詢的對比****IN 的工作方式**&#xff1a;**關鍵差異**&#x…

## DeepSeek寫水果記憶配對手機小游戲

DeepSeek寫水果記憶配對手機小游戲 提問 根據提的要求&#xff0c;讓DeepSeek整理的需求&#xff0c;進行提問&#xff0c;內容如下&#xff1a; 請生成一個包含以下功能的可運行移動端水果記憶配對小游戲H5文件&#xff1a; 要求 可以重新開始游戲 可以暫停游戲 卡片里的水果…

【含文檔+PPT+源碼】基于Django框架的鄉村綠色農產品交易平臺的設計與實現

項目介紹 本課程演示的是一款基于Django框架的鄉村綠色農產品交易平臺的設計與實現&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Python學習者。 1.包含&#xff1a;項目源碼、項目文檔、數據庫腳本、軟件工具等所有資料 2.帶你從零開始部署運…

idea超級AI插件,讓 AI 為 Java 工程師

引言? 用戶可在界面中直接通過輸入自然語言的形式描述接口的需求&#xff0c;系統通過輸入的需求自動分析關鍵的功能點有哪些&#xff0c;并對不確定方案的需求提供多種選擇&#xff0c;以及對需求上下文進行補充&#xff0c;用戶修改確定需求后&#xff0c;系統會根據需求設…