老張:「喂,小李。對,叫你呢,別看了,你那塊板子上的馬達再抖下去,就要原地起飛了。過來一下。」
老張:「你看你這個表情,跟便祕三天一樣。是不是覺得這顆 C2000 微控制器很秋條?主頻跑個幾百兆,功能一大堆,結果你一個小小的控制迴路,CPU 佔用率就快頂到天花板了?主程式那邊還想做個漂亮的 UI 跟通訊?門都沒有。」
老張:「我跟你說,這就是典型的『沒摸熟架構就想一步登天』的症頭。你以為德州儀器(TI)那群人是吃素的?他們早就想到這問題了。來,茶杯拿著,我今天心情好,跟你講個東西,叫做『Control Law Accelerator』,控制律加速器,簡稱 CLA。」
(罐頭音效:登登登登~ 氣勢磅礡的音樂)
老張:「你不要聽到『加速器』三個字就高潮了,它不是什麼氮氣瓶,按下去你的程式碼就會跑得跟火箭一樣快。你要把它想像成,你,也就是主CPU,是個大老闆,每天要管公司營運、要看財報、要跟客戶吃飯、還要應付老闆的老闆,事情多得要死。你很忙,對吧?」
老張:「現在,公司有個部門,專門負責一個極度重要、極度規律、但又極度無聊的工作,比如說,每毫秒要精準地調整一次生產線上的馬達轉速。這個工作不能等,你一分心,馬達可能就燒了,或是產品就歪了。你這個大老闆,總不能為了這件鳥事,把所有客戶的電話都掛掉吧?」
老張:「CLA 就是你請來的一個超級書呆子,一個數學宅男。你把他關在一個小房間裡,跟他說:『嘿,宅男,從現在開始,生產線上的所有數學運算,ADC 採樣一進來,你就要立刻、馬上、'Just-in-time' 地給我算完,然後把結果丟給 PWM 模組。做完你就可以繼續發呆,沒做完你就死定了。其他所有跟客戶溝通、跟老闆屁話的事情,都交給我這個大老闆來處理。』」
所以啦,簡單一句話:CLA 就是讓你把 C28x CPU 從水深火熱的即時控制迴圈中解放出來的超級賽亞人! 它能做到「Just-in-time」讀取 ADC 採樣,大幅縮短從採樣到輸出的延遲,讓你的系統反應跟閃電一樣快,控制迴圈頻率也能拉得更高。聽起來很香對不對?
(罐頭音效:烏鴉飛過… 啊… 啊… 啊…) -> (老張:喂!這音效不對吧!)
老張:「好,概念懂了,接下來就是最噁心的部分了:你怎麼叫這個書呆子開始工作?你以為遞張紙條給他就行了?太天真了。你需要一套完整的SOP,從幫他劃定辦公區域、給他工作手冊、到教他怎麼看懂門口的提示燈。這就是我們今天要搞的,CLA 的初始化流程。你筆記本拿出來,接下來我說的每個字,都可能是你未來加班到半夜時,能救你一命的救命毫毛。」
它有自己的一套架構,跟 C28x CPU 各玩各的,但又能緊密合作。它有自己的:
它的指令集可是專為控制演算法設計的,包含:
CLA 最多可以處理八個獨立的「任務 (Task)」。你可以把它想像成 CLA 有八個專案可以同時接,但一次只做一個專案裡的一件事。
MVECT
相關暫存器指定。IACK
指令強迫 CLA 開始某個任務。老張:「這是所有新手的第一个天坑,也是最大的坑。你記住,CLA 跟主CPU雖然在同一顆晶片上,但他們預設是老死不相往來的。他們共用一些叫做 LSxRAM 的記憶體區塊,你可以把它想像成辦公室裡的幾塊共用大白板。」
老張:「這一步通常在你的主程式初始化函式裡面做,比如用 memcpy
之類的指令,把儲存在 Flash 裡面的 CLA 程式,複製到 RAM 裡面。為什麼要複製?因為 CLA 這書呆子動作太快,他沒空去慢吞吞的 Flash 裡面拿資料,他只看 RAM 裡的東西。」
老張:「記住這個許可權,寫反了?沒事,系統會直接忽略你的寫入操作,但你就會在那裡抓破頭想說為什麼你的資料一直沒更新。這就是第二個坑:搞不清楚誰能寫哪個信箱。」
老張:「好,地盤劃好了,信箱也準備好了。第二步:撰寫工作手冊,也就是 CLA 程式碼。... 第六步:開綠燈,啟用中斷。我們回顧一下:」
這個初始化流程,基本上都是由 C28x 主 CPU 來完成的。菜鳥們,筆記本拿出來,這段 SOP 很重要,搞錯一步,CLA 就躺平給你看,到時候 debug 到天荒地老,別說我沒提醒你!
你寫好的 CLA 程式,CPU 的任務就是把它複製到指定的 LSxRAM 區塊。
菜鳥OS:「為什麼不直接讓 CLA 從 Flash 讀?」老張:「傻孩子,Flash 比較慢啊!CLA 這種急驚風,當然要用 RAM 才跑得快!」
如果你的 CLA 程式需要一些預設的常數、查找表、或是初始化的資料緩衝區,CPU 也要把它們填到指定的 LSxRAM 區塊。
這個步驟最繁瑣,但也最關鍵。就像幫新員工設定電腦權限一樣。
PCLKCRn
暫存器,把 CLA 的時脈打開。MVECT1
到 MVECT8
。要把每個 CLA 任務程式的起始位址填到對應的 MVECTx
。
老張:「這樣,當 Task 1 被觸發時,CLA 就會去看 MVECT1
裡面的地址,然後跑到那個地址開始讀取指令。如果你忘了設,或是設錯了,CLA 就會跑到一個鳥不生蛋的地方去執行程式,後果你懂的。這是第五個坑。」
DmaClaSrcSelRegs.CLA1TASKSRCSELx[TASKx]
,告訴 CLA 哪個周邊事件對應到哪個任務。
老張:「要做到這點,你需要設定一個叫做 DmaClaSrcSelRegs
的暫存器。這名字很長,但你拆開看:DMA CLA Source Select Registers。你可以把它想像成一個總機接線板...你就在這個接線板上,用跳線把『ADC A 中斷 1』連到『CLA Task 1』。這個『跳線號碼牌』就是你要填到暫存器裡的值。每個號碼牌對應一個特定的周邊中斷源。這些號碼牌寫在哪?當然是在你那本比磚頭還厚的 TRM (Technical Reference Manual) 裡面啦!你以為我會背給你聽?自己去翻,查那個 CLA 章節裡的『Configuration Options』表格,通常是 Table 7-1 那一大串!」
老張:「喏,怕你懶得翻,我把那張落落長的『CLA 任務召喚獸對照表』貼給你看。這張表就是告訴你,你要讓哪個周邊的哪個事件去叫醒 CLA 的哪個任務,你就要在 DmaClaSrcSelRegs.CLA1TASKSRCSELx[TASKx]
這個暫存器裡面填上對應的『Select Value』。看清楚啦,這可是考試重點!」
Select Value | CLA Trigger Source |
---|---|
0 | CLA_SOFTWARE_TRIGGER |
1 | ADCAINT1 |
2 | ADCAINT2 |
3 | ADCAINT3 |
4 | ADCAINT4 |
5 | ADCA_EVT_INT |
6 | ADCBINT1 |
7 | ADCBINT2 |
8 | ADCBINT3 |
9 | ADCBINT4 |
10 | ADCB_EVT_INT |
11 | ADCCINT1 |
12 | ADCCINT2 |
13 | ADCCINT3 |
14 | ADCCINT4 |
15 | ADCC_EVT_INT |
16-28 | Reserved |
29 | XINT1 |
30 | XINT2 |
31 | XINT3 |
32 | XINT4 |
33 | XINT5 |
34-35 | Reserved |
36 | EPWM1_INT |
37 | EPWM2_INT |
38 | EPWM3_INT |
39 | EPWM4_INT |
40 | EPWM5_INT |
41 | EPWM6_INT |
42 | EPWM7_INT |
43 | EPWM8_INT |
44 | EPWM9_INT |
45 | EPWM10_INT |
46 | EPWM11_INT |
47 | EPWM12_INT |
48 | EPWM13_INT |
49 | EPWM14_INT |
50 | EPWM15_INT |
51 | EPWM16_INT |
52 | MCANA_FEVT0 |
53 | MCANA_FEVT1 |
54 | MCANA_FEVT2 |
55 | MCANB_FEVT0 |
56 | MCANB_FEVT1 |
57 | MCANB_FEVT2 |
58 | EPWM17_INT |
59 | EPWM18_INT |
60-67 | Reserved |
68 | CPU_TINT0 |
69 | CPU_TINT1 |
70 | CPU_TINT2 |
71-74 | Reserved |
75 | ECAP1_INT |
76 | ECAP2_INT |
77 | ECAP3_INT |
78 | ECAP4_INT |
79 | ECAP5_INT |
80 | ECAP6_INT |
81 | ECAP7_INT |
82 | Reserved |
83 | EQEP1_INT |
84 | EQEP2_INT |
85 | EQEP3_INT |
86 | EQEP4_INT |
87 | EQEP5_INT |
88 | EQEP6_INT |
89-91 | Reserved |
92 | ECAP6_INT2 |
93 | ECAP7_INT2 |
94 | Reserved |
95 | SD1_ERRINT |
96 | SD2_ERRINT |
97 | SD3_ERRINT |
98 | SD4_ERRINT |
99 | LINA_INT1 |
100 | LINA_INT0 |
101 | LINB_INT1 |
102 | LINB_INT0 |
103 | ECAT_SYNC0 |
104 | ECAT_SYNC1 |
105 | PMBUSA_INT |
106-108 | Reserved |
109 | SPIA_TXINT |
110 | SPIA_RXINT |
111 | SPIB_TXINT |
112 | SPIB_RXINT |
113 | SPIC_TXINT |
114 | SPIC_RXINT |
115 | SPID_TXINT |
116 | SPID_RXINT |
117 | CLB5_INT |
118 | CLB6_INT |
119-120 | Reserved |
121 | CLA_INTERRUPT1 |
122 | Reserved |
123 | FSITXA_INT1 |
124 | FSITXA_INT2 |
125 | FSIRXA_INT1 |
126 | FSIRXA_INT2 |
127 | CLB1_INT |
128 | CLB2_INT |
129 | CLB3_INT |
130 | CLB4_INT |
131-142 | Reserved |
143 | SD1FLT1_DRINT |
144 | SD1FLT2_DRINT |
145 | SD1FLT3_DRINT |
146 | SD1FLT4_DRINT |
147 | SD2FLT1_DRINT |
148 | SD2FLT2_DRINT |
149 | SD2FLT3_DRINT |
150 | SD2FLT4_DRINT |
151 | SD3FLT1_DRINT |
152 | SD3FLT2_DRINT |
153 | SD3FLT3_DRINT |
154 | SD3FLT4_DRINT |
155 | FSITXB_INT1 |
156 | FSITXB_INT2 |
157 | FSIRXB_INT1 |
158 | FSIRXB_INT2 |
159 | FSIRXC_INT1 |
160 | FSIRXC_INT2 |
161 | FSIRXD_INT1 |
162 | FSIRXD_INT2 |
163-183 | Reserved |
184 | DMA_CH1INT |
185 | DMA_CH2INT |
186 | DMA_CH3INT |
187 | DMA_CH4INT |
188 | DMA_CH5INT |
189 | DMA_CH6INT |
190-201 | Reserved |
202 | SD4FLT1_DRINT |
203 | SD4FLT2_DRINT |
204 | SD4FLT3_DRINT |
205 | SD4FLT4_DRINT |
206-255 | Reserved |
IACK
指令來啟動 CLA 任務,記得要把 MCTL
暫存器裡的 IACKE
位元設起來。老張:「接下來,你要做一個非常重要的動作:『交出所有權』。你要對著記憶體控制器大喊:『嘿!從現在開始,這幾塊白板,是給 CLA 專用的了!我主CPU沒事不會去亂碰!』這個『喊話』的動作,就是去設定幾個特殊的暫存器。LSxMSEL
跟 LSxCLAPGM
。」
MemCfgRegs.LSxMSEL[MSEL_LSx]
位元為 1。然後,寫入 MemCfgRegs.LSxCLAPGM[CLAPGM_LSx]
位元為 0 (資料記憶體)。
MemCfgRegs.LSxMSEL[MSEL_LSx]
為 1。然後,重點來了,要寫 MemCfgRegs.LSxCLAPGM[CLAPGM_LSx]
為 1 (程式記憶體)。
LS0MSEL=1
, LS0CLAPGM=1
。再去設 LS1RAM 的 LS1MSEL=1
, LS1CLAPGM=0
。搞混了會怎樣?呵呵,輕則 CLA 罷工,重則『程式跑飛』,那時候你只能拔電源了。」
MIER
暫存器) 最好先全部清成 0。等所有東西都設定妥當了,最後才打開。當 CLA 完成一個任務時,它會在 PIE 模組產生一個對應的中斷訊號給 CPU。CPU 這邊也要設定好 PIE。
老張:「萬事俱備,只欠東風。最後一步,就是走到 CLA 辦公室門口,把「允許打擾」的牌子掛上去。這個動作是設定 MIER
(Interrupt Enable Register) 暫存器。你把對應 Task 1 的位元設成 1,才算是真正啟用了這個任務的中斷。」
例如,如果你設定 ADC 轉換完成後觸發 CLA 任務,那就要去設定 ADC 的相關參數、啟用 ADC 中斷等等。
老張:「做完這些,你的主CPU就可以去泡茶看報紙了(當然是開玩笑的,你還有別的活要幹),CLA 就會像個忠誠的僕人一樣,在背景幫你處理那些高速迴路。」
老張:「CLA 有自己的一套指令集,是專為數學運算優化的。你可以用組合語言來寫,如果你想體驗一下石器時代工程師的快感。但現在都什麼年代了,TI 早就提供了 CLA 的 C 編譯器。你可以用 C 語言的一個子集來寫 CLA 的程式。」
你只要建立一個 .cla
結尾的檔案,然後在裡面寫 C 語言函式就行了。不過,這個 C 不是你平常寫的那個萬能 C。它沒有那麼多花俏的功能,主要是做數學運算。別想在裡面搞什麼動態記憶體配置 malloc
,或是印東西 printf
,書呆子沒那麼多才多藝。
在你的 .cla
檔案裡,你會定義好幾個「任務」(Task)。CLA 最多可以有八個任務,從 Task1 到 Task8。每個任務都是一個獨立的函式,函式前面要加上 __attribute__((interrupt))
這個宣告。
__attribute__((interrupt))
void Cla1Task1 ( void )
{
// 在這裡寫你的高速控制迴路程式碼
// 讀取 ADC 結果
// 執行 PID 運算
// 更新 PWM 佔空比
__mstop(); // 告訴書呆子這件工作做完了
}
__mstop()
了嗎?這非常重要!這是書呆子工作手冊的最後一頁,告訴他:『事情做完了,可以休息了。』如果你忘了加這個,他做完事之後不知道要幹嘛,就會繼續往下執行記憶體裡的垃圾數據,然後... 砰!又是『程式跑飛』套餐。這是第三個坑。」
當然,如果你是個追求極致效能的硬核玩家,直接手刻 CLA 組合語言也是可以的。
CLA 埋頭苦幹,算出結果總得讓 CPU 知道吧?CPU 有新的命令或參數要給 CLA,也得有管道啊!這時候,「訊息 RAM (Message RAMs)」就登場了。
老張:「記住這個許可權,寫反了?沒事,系統會直接忽略你的寫入操作,但你就會在那裡抓破頭想說為什麼你的資料一直沒更新。」
老張:「接下來,講點進階的,也是坑中之坑:Pipeline,管線。」
好啦,基本操作你們應該有點概念了。但江湖險惡,CLA 也不是省油的燈。有些小細節沒注意,它可是會毫不留情地把你搞死。
LSxMSEL
(記憶體擁有者) 和 LSxCLAPGM
(程式/資料屬性) 這兩個位元,一定要設對!LSxMSEL=1
),CLA 就讀不到它的程式和資料。LSxCLAPGM
設錯,CLA 也會一臉懵逼。老張:「喔喔喔!這位同學問到重點了!EALLOW / MEALLOW 保護機制,這可是 C2000 微控制器家族,尤其是 CLA 在操作時,一個非常非常重要的「安全鎖」概念。搞不懂這個,你的程式輕則跑不動,重則把系統搞到天翻地覆,到時候老闆拿著電蚊拍追殺你,別說我沒警告過你啊!」
(罐頭音效:緊張刺激的鼓點聲)想像一下情境:你家有個保險箱,裡面放著傳家之寶(在 MCU 的世界裡,就是那些超級重要的系統控制暫存器)。平常這個保險箱是鎖上的,誰都不能亂動。
EALLOW
(Enable Write Access to EALLOW-Protected Registers):這就像是總經理(也就是你的 C28x 主 CPU)拿出他的專用鑰匙,「喀啦」一聲,把保險箱打開了。打開之後,總經理才能修改保險箱裡的東西。
EDIS
(Disable Write Access to EALLOW-Protected Registers):總經理修改完傳家之寶後,一定要記得「喀啦」一聲,再把保險箱鎖回去! 不然阿貓阿狗都跑進去亂搞,傳家之寶被換成狗尾巴草,你就準備回家吃自己了。
CLA 這位特種部隊成員,它也有自己的一把「保險箱鑰匙」!
MEALLOW
(CLA Enable Write Access to EALLOW Protected Registers):這是 CLA 自己要動用那些受 EALLOW 保護的暫存器時,CLA 拿出它的專用鑰匙,把保險箱打開。
MEDIS
(CLA Disable Write Access to EALLOW Protected Registers):CLA 用完之後,一樣,CLA 自己要把保險箱鎖回去!
CPU 的鑰匙歸 CPU 管,CLA 的鑰匙歸 CLA 管! 這兩套是獨立的!CPU 用 EALLOW
開鎖,不代表 CLA 也能寫;CLA 用 MEALLOW
開鎖,也不代表 CPU 就能寫。各管各的!
這是為了「保護」。那些系統控制暫存器,一旦設錯,整個系統可能就掰掰了。所以平常把它們鎖起來,只有在「確定要修改」的時候,才短暫地打開寫入權限,改完立刻鎖上。這樣可以大幅減少因為程式意外跑飛或是一些 bug 導致重要暫存器被錯誤修改的風險。
這個問題問得好!答案是:當你要寫入那些被「EALLOW 保護」的暫存器時,就必須使用!
這個嘛…種類可多了!通常是那些跟系統核心功能、時脈、重置、安全、以及很多周邊模組的關鍵設定有關的暫存器。常見的例子(但不限於此,一定要查你的晶片 TRM!):
MemCfgRegs.LSxMSEL
和 MemCfgRegs.LSxCLAPGM
MVECTx
, MVECTBGRND
)MCTL
)MIER
, MIFRC
, MICLR
等等)MCTLBGRND
)MPSACTL
)答案永遠是:查閱你那顆晶片的 Technical Reference Manual (TRM)! TRM 裡面描述每個暫存器的時候,通常會標註它是否受 EALLOW 保護。如果它標示了「Write Protection: EALLOW」,那恭喜你,CPU 要寫它的時候,就得乖乖用 EALLOW ... EDIS
包起來。如果是 CLA 要寫入這些受 EALLOW 保護的暫存器呢?那就在你的 CLA 程式碼裡面,用 MEALLOW ... MEDIS
把寫入操作包起來!
// 假設我們要設定某個受 EALLOW 保護的 GPIO MUX
// (這只是範例,具體暫存器名稱和位元請查 TRM)
// 先儲存目前的 EALLOW 狀態 (好習慣,雖然不一定每次都必要)
uint16_t eallow_status_backup = __get_EALLOW_status();
EALLOW; // 打開保險箱!
// 在這裡進行寫入操作
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1; // 假設 GPIO0 設定為某個周邊功能
// ... 其他需要 EALLOW 保護的寫入 ...
EDIS; // 鎖上保險箱!
// (可選) 還原 EALLOW 狀態
// __set_EALLOW_status(eallow_status_backup);
// 不過通常 EDIS 就夠了,除非你有特殊巢狀需求
; 假設 CLA 要修改某個受 EALLOW 保護的 ePWM 暫存器
; (這只是範例)
MEALLOW ; CLA 打開保險箱!
MMOV16 @EPwm1Regs.TBCTL.bit.CTRMODE, MR0 ; 假設 MR0 裡放著要設定的計數模式
MEDIS ; CLA 鎖上保險箱!
#include <intrinsics.h> // 可能需要引入這個或類似的標頭檔
// ... CLA C 程式碼 ...
__meallow(); // CLA 打開保險箱
// 假設 EPwm1Regs 已經被正確定義且 CLA 可以存取
EPwm1Regs.TBCTL.bit.CTRMODE = some_value_in_cla_register;
__medis(); // CLA 鎖上保險箱
// ... CLA C 程式碼 ...
EALLOW
,結尾才 EDIS
。這中間所有受保護暫存器都是「不設防」的。正確做法:只在你「真正要寫入受保護暫存器」的那幾行程式碼前後包起來,範圍越小越好!MEALLOW
,CLA 的程式用 EALLOW
。鑰匙拿錯了啦!EALLOW/EDIS
,但要小心 ISR 應該越快越好。EALLOW ... EDIS
。MEALLOW ... MEDIS
。這個機制雖然看起來有點囉嗦,但它是保護你系統穩定運行的重要防線。養成好習慣,該開鎖就開鎖,該鎖門就鎖門,這樣你的 CLA 和 CPU 才能合作無間,系統也才能長治久安,你才能準時下班回家打電動啦!
(罐頭音效:遊戲勝利的音效)老張:「懂了沒?這個 EALLOW/MEALLOW 很重要,回去多看幾遍,把它刻在腦子裡!再忘記,下次就罰你手抄 TRM 裡所有受 EALLOW 保護的暫存器清單一百遍!哼!」
老張:「你把 CLA 想像成一個工廠的流水線,一個指令從進來到完成,要經過好幾個站點...這也帶來了一些噁心人的『副作用』。」
老張:「在 CLA 的管線裡,「讀取」站點在「寫入」站點的前面...當你讀取 TBCTR 的指令進入「讀取」站點時,你那條更新 CMPA 的指令,可能還在流水線的後面晃悠,根本還沒到「寫入」站點!所以你讀到的,可能是基於舊的 CMPA 值的狀態。怎麼辦?很笨,但很有效:在寫入跟讀取之間,塞一兩個 MNOP
(No-Operation)指令。」
老張:「CLA 有一種條件跳轉指令,比如 MBCNDD
...當 MBCNDD
這條指令在『解碼』站點判斷要不要跳的時候,它後面緊跟著的三條指令,已經被送上流水線了!所以,不管最後跳不跳,這三條指令都『一定會被執行』!這三條指令佔據的位置,就叫『延遲槽』。你絕對不能在那三條指令裡放 MSTOP
!不然會發生什麼事?你猜。對,你的任務會在一個完全不該結束的地方結束。這是天坑中的無底洞。」
MNOP
。要讓一個周邊觸發 CLA 任務,你需要設定好幾層:周邊本身、CLA 的 DmaClaSrcSelRegs
、CLA 的 MIER
,以及 CPU 端的 PIE(如果需要)。
老張:「Debug CLA 程式碼也是一門藝術。因為 CLA 的執行是獨立的,你用傳統的方式在主CPU那邊設斷點,是斷不住 CLA 的。」
.cla
程式碼裡面,手動插入一個 MDEBUGSTOP
或 MDEBUGSTOP1
指令。
MDEBUGSTOP1
是比較推薦的用法。老張:「當 CLA 在一個迴圈裡瘋狂執行時...你主CPU這邊的除錯器,有時候會連不上它...唯一的辦法,就是透過主CPU,去觸發 CLA 的軟體重置(Soft Reset)或硬體重置(Hard Reset)。在 MCTL
暫存器裡有這兩個位元。把它們設一下,等於是把你那個陷入死迴圈的書呆子敲暈,然後再重新開始。」
MIRUN
位元會保持設定。只能重置 CLA。情境:ADC 即時採樣 -> 簡單濾波 -> 更新 PWM 責任週期
MVECT1
, DmaClaSrcSelRegs
, 記憶體映射, 啟用 MIER.INT1
)。
MMOVZ16 MR0, @ADCRESULTx ; 從 ADC 結果暫存器讀值到 MR0
; (執行濾波演算法,可能用到 MR1, MR2, MAR0, MAR1 等)
; (根據濾波結果計算新的 PWM 責任週期值,存在 MRx)
MEALLOW ; 如果 PWM 暫存器受保護
MMOV16 @EPWMxRegs.CMPA, MRx ; 把新算出的責任週期值寫入 ePWM
MEDIS
MSTOP ; 任務結束
這個流程裡,CPU 只需要在開頭設定好所有東西,然後就可以去做其他事情了。之後 ADC 的採樣、濾波、更新 PWM,全部由 CLA 自動完成。
CLA 還有很多有趣的玩法,例如:
喂!菜鳥,上面講那麼多,怕你記不住。這裡給你一個濃縮版的SOP,照著做,CLA 至少能跑起來。細節還是要回去看本文跟TRM啊!
.cla
檔案):
__attribute__((interrupt))
函式。__mstop();
(C 語言) 或 MSTOP
(組合語言)。__meallow() / __medis()
或 MEALLOW / MEDIS
。main()
或初始化函式中):
PCLKCRn
相關位元。memcpy
或類似方式將 CLA 程式從 Flash/連結器符號複製到指定的 LSxRAM (如 Cla1ProgRAM
)。Cla1DataRAM
)。MemCfgRegs.LSxMSEL = 1
和 MemCfgRegs.LSxCLAPGM = 1
。MemCfgRegs.LSxMSEL = 1
和 MemCfgRegs.LSxCLAPGM = 0
。EALLOW
保護。MVECTx
暫存器 (例如:MVECT1 = (uint16_t)&Cla1Task1;
)。MVECTx
寫入通常需要 EALLOW
保護。DmaClaSrcSelRegs
或 CLAxTRGSEL
(依晶片型號) 將周邊中斷 (如 ADC 中斷、ePWM 中斷) 連接到 CLA 任務。CLA_SOFTWARE_TRIGGER
(Select Value = 0)。EALLOW
保護。IACK
指令觸發 CLA,設定 MCTL
暫存器的 IACKE
位元。 (MCTL
寫入需 EALLOW
)MIER
(Interrupt Enable Register) 中對應任務的致能位元。 例如 MIER.INT1 = 1
。MIER
寫入通常需要 EALLOW
保護。MDEBUGSTOP
或 MDEBUGSTOP1
在 CLA 程式中設定軟體斷點。MCTL
中的軟體/硬體重置位元。老張最後囉嗦一句: 這個 SOP 是骨架,肉還是要靠你自己啃 TRM 跟範例程式碼才能長出來!加油,別被 CLA 這匹野馬甩下來!
老張:「好了,小李。我今天說的夠多了,口水都乾了。從記憶體、程式碼、觸發、向量、啟用,到管線天坑跟除錯技巧,CLA 的精髓跟鳥事,差不多都在這裡了。」
老張:「你不要覺得這東西很麻煩。它就像一匹野馬,脾氣很壞,但一旦你馴服了它,它能帶你跑得飛快。你的主CPU可以去做更多有價值的事情,而不用被困在每秒幾萬次的無盡迴圈裡。」
老張:「回去把你那抖得跟帕金森一樣的馬達程式碼,用我今天教你的方法,把核心的 PID 運算部分,移植到 CLA 裡面去。做對了,你會看到你的主CPU佔用率,可能從 80% 瞬間掉到 10% 以下。那種成就感,就是我們這些搞嵌入式的,在無數個加班夜晚後,還能笑得出來的唯一理由。」
TRM (Technical Reference Manual) 是你們的聖經,遇到問題多拜讀幾遍。TI 官方也提供很多範例程式 (C2000Ware 裡面翻一下 driverlib/DEVICE_GPN/examples/cla
路徑)。
老張:「去吧,別讓我失望。哦對了,順便幫我把茶杯洗一下。」
(罐頭音效:掌聲 + 歡呼聲)