硬件程序開發流程
-
相關硬件的工作原理
-
理解硬件的工作原理,明確硬件的功能和用途。
-
-
硬件連接
-
將硬件設備正確連接到開發板上。
-
-
編寫程序
-
根據硬件功能編寫相應的程序代碼。
-
-
調試驗證
-
通過調試工具驗證程序的正確性,確保硬件功能正常。
-
控制LED的步驟
-
找到LED對應的絲印
-
在開發板(PCB)上找到LED對應的絲印標識。
-
-
找到對應的器件
-
在原理圖中根據絲印找到對應的LED器件。
-
-
找到連接的處理器引腳
-
確定LED連接到處理器的哪個引腳(如GPB5)。
-
-
控制引腳輸出高低電平
-
配置引腳功能為輸出,并通過寄存器控制引腳輸出高低電平。
-
一、LED燈點亮
寄存器配置
端口B控制寄存器(GPBCON, GPBDAT, GPBUP)
寄存器 | 地址 | R/W | 描述 | 復位值 |
---|---|---|---|---|
GPBCON | 0x56000010 | R/W | 配置端口B的引腳 | 0x0 |
GPBDAT | 0x56000014 | R/W | 端口B的數據寄存器 | - |
GPBUP | 0x56000018 | R/W | 端口B的上拉使能寄存器 | 0x0 |
GPBCON寄存器位配置
位 | 描述 | 初始狀態 |
---|---|---|
GPB5[11:10] | 00 = 輸入,01 = 輸出,10 = nXBACK,11 = 保留 | 0 |
GPBDAT寄存器位配置
位 | 描述 | 初始狀態 |
---|---|---|
GPB5 | 當端口配置為輸出時,控制引腳輸出高低電平 | - |
代碼實現
1. 初始化LED
void led1_init(void)
{// 配置GPB5引腳功能為輸出gpio_cfg(GPB5, OUT);// 使GPB輸出高電平(LED滅)GPBDAT |= (0xF << 5);
}
2. LED亮燈
void led1_on(void)
{// 輸出低電平(LED亮)GPBDAT &= ~(0xF << 5);
}
3. LED滅燈
void led1_off(void)
{// 輸出高電平(LED滅)GPBDAT |= (0xF << 5);
}
4. 流水燈效果
void led1_flow(void)
{for (int i = 0; i < 4; i++){// 依次點亮不同的LEDGPBDAT |= (0xF << 5); // 所有LED滅GPBDAT &= ~(1 << (5 + i)); // 第i個LED亮delay(100000); // 延時}
}
5. 按鍵控制LED
void buttons(void)
{int eint4 = GPGDAT & 1; // 檢測EINT4按鍵狀態int eint5 = (GPFDAT >> 4) & 0x1; // 檢測EINT5按鍵狀態int eint6 = (GPFDAT >> 5) & 0x1; // 檢測EINT6按鍵狀態int eint8 = (GPFDAT >> 6) & 0x1; // 檢測EINT8按鍵狀態// 根據按鍵狀態控制對應的LEDif (eint4 == 0) GPBDAT &= ~(1 << 5); // EINT4按下,LED5亮else GPBDAT |= (1 << 5); // EINT4松開,LED5滅if (eint5 == 0) GPBDAT &= ~(1 << 6); // EINT5按下,LED6亮else GPBDAT |= (1 << 6); // EINT5松開,LED6滅if (eint6 == 0) GPBDAT &= ~(1 << 7); // EINT6按下,LED7亮else GPBDAT |= (1 << 7); // EINT6松開,LED7滅if (eint8 == 0) GPBDAT &= ~(1 << 8); // EINT8按下,LED8亮else GPBDAT |= (1 << 8); // EINT8松開,LED8滅
}
二、時鐘配置
1. 時鐘分頻寄存器 (CLKDIVN)
-
地址:
0x4C000014
-
功能: 控制HCLK和PCLK的分頻比例。
-
位域定義:
-
HCLK分頻器: 位1 (
HDIVN
),用于設置HCLK的分頻比例。 -
PCLK分頻器: 位0 (
PDIVN
),用于設置PCLK的分頻比例。
-
-
配置方法:
CLKDIVN = (0x2 << 1) | 0x1; // 設置 HCLK 分頻器為 2,PCLK 分頻器為 1
-
HCLK 分頻器: 2 (HCLK = PCLK * 3)
-
PCLK 分頻器: 1 (PCLK = MPLLCLK / 2 / 2)
-
2. 主PLL寄存器 (MPLLCON)
-
地址:
0x4C000004
-
功能: 配置主PLL的輸出時鐘頻率。
-
位域定義:
-
M值: 位15-12 (
MDIV
),用于設置主分頻器。 -
P值: 位11-8 (
PDIV
),用于設置預分頻器。 -
S值: 位7-4 (
SDIV
),用于設置后分頻器。
-
-
計算公式:
-
MPLL輸出頻率:
(M + 8) * Fin / (P * S)
-
其中:
-
M = MDIV + 8
-
P = PDIV + 2
-
S = SDIV + 2
-
Fin = 外部時鐘輸入頻率 (通常為 12 MHz)
-
-
-
配置方法:
MPLLCON = (127 << 12) | (2 << 4) | 1; // 設置 M=127, P=2, S=1
-
M值: 127 + 8 = 135
-
P值: 2 + 2 = 4
-
S值: 1 + 2 = 3
-
計算結果: MPLL 輸出頻率 =
(135) * 12 MHz / (4 * 3) = 135 MHz
-
3. 時鐘頻率計算
-
MPLL輸出頻率: 135 MHz
-
HCLK頻率: 135 MHz / (HCLK分頻器 + 1) = 135 MHz / 3 = 45 MHz
-
PCLK頻率: 135 MHz / (2 * (PCLK分頻器 + 1)) = 135 MHz / (2 * 2) = 33.75 MHz
代碼實現
#include "clk.h"
#include <s3c2440.h>// 初始化時鐘配置
void clk_init(void)
{// 配置時鐘分頻寄存器 (CLKDIVN)// 設置 HCLK 分頻器為 2,PCLK 分頻器為 1CLKDIVN = (0x2 << 1) | 0x1;// 配置主PLL寄存器 (MPLLCON)// 設置 M=127, P=2, S=1MPLLCON = (127 << 12) | (2 << 4) | 1;
}
三、定時器配置
1. 配置 GPIO 引腳為 PWM 模式
GPBCON &= ~(0x03 << 0); // 清除 GPB0 和 GPB1 的配置
GPBCON |= (0x02 << 0); // 將 GPB0 配置為 nPWM0 功能
-
功能:將 GPB0 引腳配置為 PWM 功能(nPWM0)。
-
邏輯:
-
GPBCON
是 GPIO 控制寄存器,低兩位控制 GPB0 和 GPB1 的功能。 -
使用位操作清除低兩位(
0x03 << 0
),然后設置為0x02 << 0
,將 GPB0 配置為 PWM 功能。
-
2. 配置定時器預分頻器和分頻器
TCFG0 = 24; // 設置定時器0的預分頻值為24
-
功能:設置定時器 0 的預分頻值。
-
邏輯:
-
TCFG0
是定時器配置寄存器,Prescaler0
決定定時器 0 和 1 的預分頻值。 -
預分頻值設置為 24,定時器輸入時鐘頻率計算公式為:
定時器輸入時鐘頻率=(預分頻值+1)PCLK? -
分頻值默認為 1(未顯式設置)。
-
3. 配置定時器時鐘源選擇
TCFG1 &= ~(0xf); // 清除 MUX0 的低4位,選擇默認時鐘源
-
功能:選擇定時器 0 的時鐘源。
-
邏輯:
-
TCFG1
是定時器時鐘源選擇寄存器,MUX0
控制定時器 0 的時鐘源。 -
清除
MUX0
的低 4 位(0xf
),選擇默認時鐘源(內部時鐘源,分頻值為 1/2 PCLK)。
-
4. 設置定時器計數值和比較值
TCNTB0 = 2000; // 設置定時器0的計數值
TCMPB0 = 1000; // 設置定時器0的比較值(占空比50%)
-
功能:設置定時器 0 的計數值和比較值。
-
邏輯:
-
TCNTB0
是定時器 0 的計數緩沖寄存器,設置計數值為 2000。 -
TCMPB0
是定時器 0 的比較緩沖寄存器,設置比較值為 1000。 -
PWM 周期計算公式:
PWM?周期=(TCNTB0+1)×(預分頻值+1)×分頻值 -
占空比計算公式:
占空比=TCNTB0TCMPB0?×100% -
本例中占空比為 50%(1000/2000 = 0.5)。
-
5. 配置定時器控制寄存器 TCON
TCON &= ~(1 << 4); // 禁用死區功能
TCON |= (1 << 3); // 啟用自動重載模式
TCON |= (1 << 2); // 啟用輸出變相功能
TCON |= (1 << 1); // 手動更新定時器寄存器
TCON &= ~(1 << 1); // 清除手動更新位(必須在下次寫操作前清零)
-
功能:配置定時器 0 的控制寄存器。
-
邏輯:
-
死區功能:清除
TCON
的第 4 位(bit4=0
),禁用死區功能。 -
自動重載模式:設置
TCON
的第 3 位(bit3=1
),啟用自動重載模式。 -
輸出變相功能:設置
TCON
的第 2 位(bit2=1
),啟用輸出變相功能。 -
手動更新:設置
TCON
的第 1 位(bit1=1
),手動更新定時器寄存器。 -
清除手動更新位:必須在下次寫操作前清零
bit1
。
-
6. 啟動定時器 0
TCON |= (1 << 0); // 啟動定時器0
-
功能:啟動定時器 0。
-
邏輯:
-
設置
TCON
的第 0 位(bit0=1
),啟動定時器 0。
-
四、ADC配置
1. ADC 初始化函數?adc_init
void adc_init(void)
{ADCCON = (1 << 14) | (49 << 6);
}
-
功能:初始化 ADC 控制寄存器
ADCCON
。 -
邏輯:
-
使能預分頻器:
-
設置
ADCCON
的第 14 位(PRSCEN
)為 1,使能預分頻器。
-
-
設置預分頻值:
-
設置
ADCCON
的第 13:6 位(PRSCVL
)為 49,調整 ADC 頻率。 -
注意:ADC 頻率應低于 PCLK 的 1/5。
-
-
其他配置:
-
默認選擇模擬輸入通道為 AIN0(未顯式設置)。
-
默認設置為正常工作模式(未顯式設置)。
-
-
2. ADC 啟動函數?adc_start
void adc_start(unsigned char ch)
{ADCCON &= ~(0x7 << 3); // 清除 SEL_MUX 位ADCCON |= (ch << 3); // 設置 SEL_MUX 位為指定通道// 使能讀啟動操作ADCCON |= (1 << 0); // ENABLE_START = 1
}
-
功能:啟動 ADC 轉換。
-
邏輯:
-
選擇模擬輸入通道:
-
清除
ADCCON
的第 5:3 位(SEL_MUX
),然后設置為指定的通道ch
。 -
ch
的取值范圍為 0-7,對應不同的模擬輸入通道(AIN0 到 XP)。
-
-
啟動 ADC 轉換:
-
設置
ADCCON
的第 0 位(ENABLE_START
)為 1,啟動 ADC 轉換。 -
注意:啟動后該位會被硬件清零。
-
-
3. ADC 數據讀取函數?adc_read
short adc_read(void)
{short value = 0;int i = 100;// 等待轉換結束while (!(ADCCON & (1 << 15)) && i--);if (i <= 0)return -1;// 讀取轉換結果value = ADCDAT0 & 0x3ff;return value;
}
-
功能:讀取 ADC 轉換結果。
-
邏輯:
-
等待轉換結束:
-
檢查
ADCCON
的第 15 位(ECFLG
),等待轉換結束標志位為 1。 -
如果超時(
i
減到 0),返回錯誤值 -1。
-
-
讀取轉換結果:
-
從
ADCDAT0
寄存器讀取轉換結果,取低 10 位(0x3ff
)作為有效數據。 -
返回讀取的值。
-
-
總結
-
初始化 ADC:
-
使能預分頻器。
-
設置預分頻值為 49,調整 ADC 頻率。
-
默認選擇 AIN0 通道和正常工作模式。
-
-
啟動 ADC 轉換:
-
根據傳入的通道號選擇模擬輸入通道。
-
啟動 ADC 轉換。
-
-
讀取 ADC 數據:
-
等待轉換結束。
-
讀取轉換結果并返回。
-
五、中斷配置
1. 外部中斷 EINT8 初始化函數?eint8_init
void eint8_init(void)
{ //配置GPIO 引腳為外部中斷模式GPGCON &= ~(0x3 << 0);GPGCON |= (0x2 << 0);//配置EINT8的觸發方式EXTINT1 &= ~(0x7 << 0);EXTINT1 |= (0x2 << 0); //設置EINT8的觸發方式為下降沿觸發//使能EINT8中斷//EINTMASK 寄存器:這是一個外部中斷屏蔽寄存器,用于控制外部中斷是否被允許。//INTMSK 寄存器:這是一個中斷屏蔽寄存器,用于控制內部中斷是否被允許。EINTMASK &= ~(1 << 8); //使能EINT8中斷INTMSK &= ~(1 << 5); //使能EINT8_23中斷
}
-
功能:初始化外部中斷 EINT8。
-
邏輯:
-
配置 GPIO 引腳:
-
清除
GPGCON
的低兩位(0x3 << 0
),然后設置為0x2 << 0
,將 GPG0 配置為外部中斷模式。
-
-
配置觸發方式:
-
清除
EXTINT1
的低三位(0x7 << 0
),然后設置為0x2 << 0
,將 EINT8 的觸發方式設置為下降沿觸發。
-
-
使能中斷:
-
清除
EINTMASK
的第 8 位(1 << 8
),使能 EINT8 中斷。 -
清除
INTMSK
的第 5 位(1 << 5
),使能 EINT8_23 中斷。
-
-
2. 處理 EINT8 中斷的函數?deal_eint8_23
void deal_eint8_23(void)
{//檢測EINT8是否發生了中斷//EINTPEND 寄存器:這是一個外部中斷掛起寄存器,用于指示當前有哪些外部中斷請求正在等待處理。if(EINTPEND & (1 << 8)){// 清除EINT8的中斷標志位 EINTPEND |= (1 << 8);//通過寫1清除EINTPEND的第八位}
}
-
功能:處理 EINT8 中斷。
-
邏輯:
-
檢測中斷:
-
檢查
EINTPEND
的第 8 位是否為 1,判斷 EINT8 是否發生了中斷。
-
-
清除中斷標志位:
-
通過寫 1 到
EINTPEND
的第 8 位,清除中斷標志位。
-
-
3. 中斷處理函數?c_deal_irq
void c_deal_irq(void)
{//獲取中斷源的偏移量unsigned int irq_num = INTOFFSET;// INTOFFSET 寄存器指示當前中斷源的偏移量switch (INTPND){case EINT8_23: deal_eint8_23(); break;default:break;} //清除中斷標志位SRCPND |= (1 << irq_num);// 通過寫 1 清除 SRCPND 中的相應位//INTPND 寄存器:這是一個中斷掛起寄存器,用于指示當前有哪些中斷請求正在等待處理。//中斷標志位:當一個中斷發生時,相應的標志位會被硬件自動置為 1。在中斷處理完成后,需要手動清除這些標志位,否則中斷系統會認為該中斷仍然未處理,無法再次觸發。//通過讀取 INTPND 寄存器的值,并將其重新寫回 INTPND 寄存器。//INTPND 里只有掛起的那一位置1,對改為寫1,硬件會自動清除其中的中斷標志位(即把標志位從 1 變為 0)。INTPND = INTPND;
}
-
功能:處理中斷請求。
-
邏輯:
-
獲取中斷源:
-
讀取
INTOFFSET
寄存器,獲取當前中斷源的偏移量。
-
-
調用處理函數:
-
根據
INTPND
寄存器的值,判斷中斷源并調用相應的處理函數(如deal_eint8_23
)。
-
-
清除中斷標志位:
-
通過寫 1 到
SRCPND
的相應位,清除源掛起標志位。 -
通過讀取
INTPND
并重新寫回,清除中斷掛起標志位。
-
-
4.注意事項
-
中斷標志位清除:必須在中斷處理完成后清除中斷標志位,否則中斷系統會認為該中斷仍然未處理,無法再次觸發。
-
中斷優先級:
INTOFFSET
寄存器指示當前中斷源的偏移量,確保中斷處理的優先級正確。 -
中斷屏蔽:通過
EINTMASK
和INTMSK
寄存器控制中斷的使能和屏蔽。
六、UART配置
什么是UART?
通用異步接收器/發送器,通常稱為UART,是一種廣泛應用于嵌入式領域的串行、異步、全雙工通信協議。
串口連接
UART 通道有兩條數據線。每個設備上都有一個 RX 引腳和一個 TX 引腳(RX 用于接收,TX 用于發送)。每個設備的 RX 引腳都連接到另一個設備的 TX 引腳。請注意,沒有共享時鐘線!這是通用異步接收方發送方的“異步”方面。
1. UART 初始化
1.1 引腳配置
-
將
GPHCON
的第 4 位清零,配置為 GPIO 模式。 -
設置
GPHCON
的第 4 位為0xA
,配置為 UART 模式。
1.2 線路控制寄存器 (ULCON0) 配置
-
清除第 6 位,禁用紅外模式。
-
清除第 3-5 位,設置為無奇偶校驗。
-
清除第 2 位,設置每幀 1 個停止位。
-
設置第 0-1 位為
0x3
,配置數據位為 8 位。
1.3 控制寄存器 (UCON0) 配置
-
清除第 10-11 位,選擇 PCLK 作為時鐘源。
-
清除第 0-3 位,設置為普通模式。
-
設置第 0-3 位為
0x5
,配置為中斷請求模式。
1.4 FIFO 控制寄存器 (UFCON0) 配置
-
設置第 0 位為 1,使能 FIFO。
-
設置第 1 位為 1,復位 Rx FIFO。
-
清除第 4-5 位,設置 Rx FIFO 觸發深度為 8 字節。
-
設置第 7 位為 1,復位 Tx FIFO。
1.5 波特率分頻寄存器 (UBRDIV0) 配置
-
設置
UBRDIV0
為 325,配置波特率為 9600。
1.6 中斷使能
-
清除
INTMSK
的第 28 位,使能 UART 中斷。 -
清除
INTSUBMSK
的第 0 位,使能 UART0 中斷。
// UART初始化函數
void uart_init(void)
{// 配置GPH4和GPH5為UART0功能GPHCON &= ~(0xf << 4); // 清除GPH4和GPH5的低4位,確保它們的功能未被其他用途占用GPHCON |= (0xa << 4); // 設置GPH4和GPH5為UART0功能(0xA表示UART0功能)// 配置ULCON0寄存器(UART線路控制寄存器)ULCON0 &= ~(1 << 6); // 清除第6位,確保不是同步模式ULCON0 &= ~(0x7 << 3); // 清除第3-5位,確保不是其他特殊模式ULCON0 &= ~(1 << 2); // 清除第2位,確保無奇偶校驗ULCON0 |= (0x3 << 0); // 設置低兩位為0x3,表示8位數據,無校驗,1位停止位(8N1模式)// 配置UCON0寄存器(UART控制寄存器)UCON0 &= ~(0x3 << 10); // 清除第10-11位,確保無特殊功能UCON0 &= ~(0xf << 0); // 清除低4位,確保無其他特殊功能UCON0 |= (0x5 << 0); // 設置低3位為0x5,表示接收和發送使能// 配置UFCON0寄存器(UART FIFO控制寄存器)UFCON0 |= (1 << 0); // 設置第0位,使能FIFOUFCON0 |= (1 << 1); // 設置第1位,復位接收FIFOUFCON0 &= ~(0x3 << 4); // 清除第4-5位,確保無特殊FIFO深度設置UFCON0 |= (1 << 4); // 設置第4位,設置FIFO深度為16字節UFCON0 |= (1 << 7); // 設置第7位,使能FIFO接收中斷// 配置波特率UBRDIV0 = 325; // 設置波特率分頻值,此處波特率計算為9600(具體計算公式需根據時鐘頻率確定)// 配置中斷掩碼寄存器INTMSK &= ~(1 << 28); // 使能UART0中斷INTSUBMSK &= ~(1 << 0); // 使能UART0接收中斷
}
2. 數據發送函數
2.1 函數原型
int uart0_send(unsigned char * data, unsigned char len)
2.2 功能描述
-
發送數據到 UART0。
-
循環發送數據,直到所有數據發送完成。
2.3 實現細節
-
檢查
UTRSTAT0
的第 2 位,確保發送緩沖區為空。 -
將數據寫入
UTXH0
發送緩沖區。 -
返回實際發送的數據長度。
// UART0發送數據函數
int uart0_send(unsigned char * data,unsigned char len)
{int i = 0;// 循環發送數據for(i = 0;i < len;i++){// 等待發送緩沖區為空(UTRSTAT0的第2位為1表示發送緩沖區為空)while(!(UTRSTAT0 & (1 << 2)));UTXH0 = data[i]; // 將數據寫入發送緩沖寄存器}return i; // 返回發送的數據長度
}
3. 數據接收函數
3.1 函數原型
unsigned char uart0_recv(unsigned char * data, unsigned char len)
3.2 功能描述
-
從 UART0 接收數據。
-
讀取 Rx FIFO 中的數據,直到讀取到指定長度或 FIFO 中無數據。
3.3 實現細節
-
讀取
UFSTAT0
的第 0-5 位,獲取 Rx FIFO 中的數據量。 -
比較數據量和指定長度,確定實際讀取長度。
-
從
URXH0
讀取數據,存入目標緩沖區。 -
返回實際讀取的數據長度。
// UART0接收數據函數
unsigned char uart0_recv(unsigned char * data , unsigned char len)
{int i = 0;// 獲取FIFO中數據的數量(UFSTAT0的低6位表示接收FIFO中的數據數量)unsigned char data_cnt = UFSTAT0 & 0x3f;// 計算實際接收的數據長度(取len和data_cnt的較小值)unsigned char len_r = (len < data_cnt) ? len :data_cnt;// 循環讀取數據for(i = 0; i < len_r; i++){data[i] = URXH0; // 從接收緩沖寄存器讀取數據}return i; // 返回實際接收的數據長度
}