---
# System prepended metadata

title: 快速上手新唐RTU980 Chili開發板及應用建立

---

# 快速上手新唐RTU980 Chili開發板及應用建立

## 前言
:::spoiler 更新紀錄
|更新日期|內容|
|---|---|
|2023/06/12|shell script 撰寫，自動開機執行(未完成)|
|2024/10/05|準備打掉重練|
|2024/10/07|小幅度修改|
|2024/10/12-15|新增開發流程簡要說明及工具、ioctrl部分說明，還有大頭貼|
|2024/10/18</br>2024/11/3|新增html script相關說明|
|2024/11/10|script 說明|
|2024/11/26|新增Key Cgi部分內容|
:::
本人於2023年6月初開始撰寫本篇筆記，因為種種因素(學校、工作等等)維持不到10天就放棄了。
2024開始，我重新利用這片開發板進行嵌入式開發的學習，如果有任何錯誤或指教，歡迎各位客官發郵件給我。

目標是希望能利用這塊開發板學習buildroot、應用開發，再結合過往學習的硬體知識，帶領讀者從繪製原理圖開始到layout，完整做出專案或專題。

## 一、簡介RTU980 Chili開發板

在剛拿到一個產品(或開發板)時，最重要的第一步，就是先認識上面的各種接口、介面或腳位。

如下圖(出自官方[使用者手冊](https://www.nuvoton.com/export/resource-files/en-us--UM_NuMaker_RTU_NUC980_EN_Rev1.21.pdf))
![image](https://hackmd.io/_uploads/B1y1aYZyke.png =430x400)


新唐官方有提供[使用者手冊](https://www.nuvoton.com/export/resource-files/en-us--UM_NuMaker_RTU_NUC980_EN_Rev1.21.pdf)，其中除了開發板的使用說明外，也包含了原理圖以及電路板佈局(PCB Placement)。

而我相信，各位看官應該有足夠的能力依靠官方給的[資料(拜託點這裡看一下有哪些參考資料)](https://www.nuvoton.com/products/iot-solution/iot-platform/numaker-rtu-nuc980/index.html)以及影片教學，成功啟動官方提供的應用。

>這些是官方提供的影片教學
>[新唐 Chili Board – 開發板與平台介紹](https://www.youtube.com/watch?v=tkYXZoYUY68)
>[新唐 Chili Board (1) – 下載、編譯與燒錄教學](https://www.youtube.com/watch?v=9W8HGrzjGZ8)
>[新唐 Chili Board (2) – 遠端監控與資料存取實例操作](https://www.youtube.com/watch?v=5640d9NxlCs)

在這篇筆記中，我並不打算提太多關於官方示例的內容，但還是稍微說一下。

## 二、開發流程簡要說明及工具

### 硬體工具
* 需要準備兩條Micro Usb線，或是你可以像我一樣，把一條USB A直接焊上去(帶出門很方便)。
其實在燒錄完之後，只會頻繁用到其中一個USB port，所以也可以常備一條就好。
![S__73842715](https://hackmd.io/_uploads/S1QwqW_1kg.jpg =400x400)
這兩個Micro USB孔的作用，一個是作為COM Port，讓你能透過電腦跟開發版溝通，在開發階段能快速進入系統下達指令(Virtual COM)；另一個是將系統image燒錄進去NOR Flash用的(USB0 Device)。

* 電腦一台(必須)
* 一些杜邦線(非必須)

### 軟體工具
* NuWriter(官方提供)：官方提供的燒錄軟體。
* Linux Virtual Machine(官方提供)：官方已經建置好的開發環境。
* Putty(自行下載)：透過Virtual COM跟開發板溝通的軟體介面。

### 開發流程簡要說明
![12200](https://hackmd.io/_uploads/H15upGOJ1l.png =840x250)

* 步驟一、利用官方提供的「linux虛擬機」，對「NUC980 Chili」預設的「Buildroot包」進行編譯。
* 步驟二、將編譯出的檔案移到window系統中
* 步驟三、接上兩條Micro USB線，開啟「Putty」連接Virtual COM，查看開發板輸出的信息。
* 步驟四、使用「NuWriter」將編譯完的檔案燒錄進去開發板。

## 三、針對官方示例二(IO控制)進行解說
在示例"新唐 Chili Board (2) – 遠端監控與資料存取實例操作"中，我們能透過網頁控制開發板的LED，以及接收按鈕信號。

> 實際上的控制畫面
> ![image](https://hackmd.io/_uploads/S1d4aYWkJe.png =400x300)


這邊假設各位已經能找到網頁以及IO控制的原始碼，若您沒有頭緒，請點開下面的詳細資料：
:::spoiler *1.透過Buildroot取得示例2的原始碼*
我們可以透過新唐提供的Linux映像檔，建立開發環境，並參考Buildroot(RTU980 Chili預設配置)中的output，找到示例2的原始碼；或是透過開發板上的系統來取得示例2的原始碼，以下將分為兩個部分說明。

我們安裝完新唐提供的Linux映像後，需要將原先的預設配置(應該是NUC970)改為RTU980 Chili，以下是RTU980 Chili的預設配置。
*如果各位需要相關資訊或更詳細的說明請點這裡"[NUC980 Linux Environment on VMware](https://www.nuvoton.com/export/resource-files/zh-tw--UM_NUC980_Linux_environment_on_VMware_EN_Rev1.02.pdf)"，可直接參考3.1.2*
![image](https://hackmd.io/_uploads/SJB3GeJkyg.png)

改為使用RTU980 Chili的預設配置，然後進行第一次編譯，如下圖
![image](https://hackmd.io/_uploads/Skz-Sxykke.png)

然後你需要經過漫長的等待...

你可以花點時間看外國人介紹台灣茶
{%youtube vAPiWVxe-5I%}

你可以自己去泡壺茶
![image](https://hackmd.io/_uploads/BkQlYxkyJg.png)

甚至可以閒到用小畫家畫一杯茶
![image](https://hackmd.io/_uploads/Sy7BpxJkyx.png)


等到天荒地老，終於編譯完之後，你就可以在這裡找到：

![image](https://hackmd.io/_uploads/Hk3kqxyJJx.png)

:::

:::spoiler 2.透過開發板取得示例2的原始碼
![image](https://hackmd.io/_uploads/r1B70k11yx.png =560x300)

> 如果想要參閱IO控制的網頁原始碼，可以參閱/usr/local/sbin/www/ioctrl.html
> 
:::

在嵌入式開發來說，[CGI(Common Gateway Interface (附上教學))](https://youtube.com/playlist?list=PLDhd2asKgB6Wo2uxQS3C9PBG_wjNGQLnP&si=CxYe9G0iLkjEHs9U)是一種常用來讓網頁跟硬體/系統交換訊息的後端程式。

在這裡我們針對三個比較重要的檔案進行說明：
* ioctrl.html
* key_cgi.c
* led_cgi.c

### ioctrl.html
> HTML（HyperText Markup Language，超文本標記語言）是打造網頁的基石。它表述並定義網頁的內容。伴隨 HTML 而來的技術還有描述網頁外觀（CSS）及功能性的程式語言（JavaScript）。—— [mdn web docs_](https://developer.mozilla.org/zh-TW/docs/Web/HTML)

*所以我們可以大致分成三個部份來說明，分別是網頁內容、外觀以及功能性。*

由於本人並不是軟體/網頁相關從業人員，入門的網頁教學還要請各位參考彭彭老師的[JavaScript 網頁前端工程入門教學](https://youtube.com/playlist?list=PL-g0fdC5RMbpqZ0bmvJTgVTS4tS3txRVp&si=jKFbxWCJGA0cVKCP)(超有料超簡單)。

這裡先附上完整原始碼。


:::spoiler *展開完整原始碼*
```html=
<html>
    <head>
        <title>Control IO by Web</title>
        <script type="text/javascript" src="jquery-3.5.1.js"></script>
        <style type="text/css">
            div#led_content{
                position: fixed;
                left:510px;
                top:20px;
            }

            div#key_content{
                position: fixed;
                left:20px;
                top:20px;
            }

            input[type="submit"] {
                position: fixed;
                left:750px;
                top:100px;
            }

            img {
                position: fixed;
                left: 40px;
                top: 95px;
            }
        </style>
    </head>
    <body>

        <div id="led_content">
            <form id="led_form" action="cgi-bin/led.cgi" method="GET">
                <table width="300" border="1">
                   <tr>
                        <td>LED1</td>
                        <td>LED2</td>
                    </tr>
                    <tr>
                        <td>
                            <input type="radio" id="LED1" name="LED1" value="1">ON <br>
                            <input type="radio" id="LED1" name="LED1" value="0" checked>OFF
                        </td>
                        <td>
                            <input type="radio" id="LED2" name="LED2" value="1">ON <br>
                            <input type="radio" id="LED2" name="LED2" value="0" checked>OFF
                        </td>
                    </tr>
                </table>
                <br>
                <input type="submit" id="led_submit" value="Submit" />
            </form>
        </div>

        <div id="ledimg_content">
            <img id="led_img" src="LED_1_off_2_off.jpg">
        </div>

        <div id="key_content">
            <form id="key_form" action="cgi-bin/key.cgi" method="GET">
                <table width="300" border="1">
                    <tr>
                        <td>KEY1</td>
                    </tr>
                    <tr>
                        <td>
                            <input type="radio" id="KEY1" name="KEY1" value="1">ON <br>
                            <input type="radio" id="KEY1" name="KEY1" value="0" checked>OFF
                        </td>
                    </tr>
                </table>
                <br>
            </form>
        </div>

        <script type="text/javascript">
            $(document).ready(function() {
                window.setInterval(function() {
                    key_autoload();
                }, 300);
            }); //END $(document).ready()

            var frm = $('#led_form');
            frm.submit(function(e){
                e.preventDefault();
                $.ajax({
                    type: frm.attr('method'),
                    url: frm.attr('action'),
                    data: frm.serialize(),
                    success: function(data){
                        $('#ledimg_content').html(data);
                    }
                });
            });

            var key_frm = $('#key_form');
            function key_autoload(){
                $.ajax({
                    type: key_frm.attr('method'),
                    url: key_frm.attr('action'),
                    success: function(data){
                        $('#key_content').html(data);
                    }
                });
            }
        </script>
    </body>
</html>

```
:::

#### 網頁內容
![image](https://hackmd.io/_uploads/H1IxoXuJJx.png =500x400)
> 這裡會稍微粗淺的提到一些html的語法，有錯再麻煩各位網頁大老糾正我。

舉幾個例子給各位：

:::spoiler *"按鈕狀態表格"(紅框)*
在網頁元素中，**div是一個容器**，利用div能夠更好的**排版**，或是使用**CSS**做外觀的管理。在這個div裡面，主要包含了一個**表格(table)**。

可以看到第4-6行，**tr**、**td**是表格(table) 的**行(row)跟列(colume)**，這行的意義是在表格的某一個td中**顯示"KEY1"**。
以下面原始碼的結構來看，這是在第一個tr的第一個td(第一行第一列)顯示"KEY1"，也就是紅框中的橘框表現出來的效果。

```html=
<div id="key_content"> 
    <form id="key_form" action="cgi-bin/key.cgi" method="GET"> 
        <table width="300" border="1">
            <tr>
                <td>KEY1</td> 
            </tr>
            <tr>
                <td>
                    <input type="radio" id="KEY1" name="KEY1" value="1">ON <br>
                    <input type="radio" id="KEY1" name="KEY1" value="0" checked>OFF
                </td>
            </tr>
        </table>
        <br>
    </form>
</div>
```
:::
:::spoiler *"送出LED設定按鈕"(紫框)*
以下的原始碼表現的是右邊的"LED狀態設定表格"跟"送出按鈕"的區塊，他們被包在同一個div中。

可以看到，這裡的3-18行又是一個表格，但在4-7行的**第一個tr**中，包含了**兩個td**(LDE1、LDE2)，能對應到圖片中的**第一行第一列**跟**第二行第一列**。

第20行是一個**input標籤**，**type是'submit'**，類似於'button'的功用；**'value'則是用來調整按鈕中的文字**。也就是紫框表現出來的效果。

[mdn web docs 參考資料](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input)
![image](https://hackmd.io/_uploads/BkIoVc5yyl.png =500x140)


```html=
<div id="led_content">
    <form id="led_form" action="cgi-bin/led.cgi" method="GET">
        <table width="300" border="1">
            <tr>
                <td>LED1</td>
                <td>LED2</td>
            </tr>
            <tr>
                <td>
                    <input type="radio" id="LED1" name="LED1" value="1">ON <br>
                    <input type="radio" id="LED1" name="LED1" value="0" checked>OFF
                </td>
                <td>
                    <input type="radio" id="LED2" name="LED2" value="1">ON <br>
                    <input type="radio" id="LED2" name="LED2" value="0" checked>OFF
                </td>
            </tr>
        </table>
        <br>
        <input type="submit" id="led_submit" value="Submit" />
    </form>
</div>
```
:::

:::spoiler *"開發版圖片"(綠框)*

可以看到，這邊的做法一樣是使用div作區塊的控制。
img 標籤，顧名思義就是image，將圖片]
```html=
<div id="ledimg_content">
    <img id="led_img" src="LED_1_off_2_off.jpg">
</div>
```
:::
#### 外觀
外觀常使用css來進行設計，我並不是網頁方面的專家或從業人員，考量到我對於css的說明或理解可能會稍微爛一些，各位客官若是想要相關的基礎知識教學，可以參考這裡[網頁前端工程入門：基礎 CSS 教學 By 彭彭](https://youtu.be/Jr7lwHnTK68?si=Q9CJCsklh-Qj1KPi)，本人就不針對外觀部分進行過多闡述。

在這個網頁範例中，針對各個不同的div(不同id)、input、img，都有對應的css進行外觀設定。
```html=
<style type="text/css">
div#led_content{
    position: fixed;
    left:510px;
    top:20px;
}

div#key_content{
    position: fixed;
    left:20px;
    top:20px;
}

input[type="submit"] {
    position: fixed;
    left:750px;
    top:100px;
}

img {
    position: fixed;
    left: 40px;
    top: 95px;
}

</style>
```
#### 功能性
終於來到最重要的部分，也就是實際上實現「讓網頁控制硬體電路」的部分。

我們先來開啟上帝視角，看看這件事情是怎麼實現的吧！

![image](https://hackmd.io/_uploads/H1c4ly1gyl.png =600x600)

透過網頁與開發板進行互動，實際上是由網頁中的「腳本(script)」來進行與後端(也就是開發板上實際控制電路行為的程式)進行互動，進而取得按鈕輸入狀態，以及控制LED燈。

**為了方便解釋，以及讓程式碼有highlight，這邊把html跟javascript的部分分開。(至於為何不擺在一起，大家可以參照一下markdown的部分。)**
**以下正式開始介紹。**

可以看到以下部分是html head，在第6行使用script標籤，使用了jquery-3.5.1.js這個Javascript Library。
> *(jquery-3.5.1.js在開發板的root file system中與 ioctrl.html在同個資料夾下，需要更新的話直接在[官網下載](https://jquery.com/)最新板替換即可)。*

關於jquery是什麼，以及為何需要導入jquery，我在網路上有找到一篇淺顯易懂的文章，供各位參考：
[jQuery是什麼，它跟JavaScript有什麼關係？它又有什麼能耐呢？——progressbar](https://progressbar.tw/posts/6)

如果你懶得點進去看，我在這邊說一下結論：
jQuery是一個很方便好用的函式庫，簡單來說就是能有效簡化代碼，省事用的好工具，目前是主流。

15行是script標籤，代表javasrcipt腳本從此開始。
被 *開始標籤(script)* 與 *結束標籤(/script)* 所包含的是用javascript編寫而成的腳本程式。

```html=
<html>
    <head>
        ...
        ...
        ...省略
        <script type="text/javascript" src="jquery-3.5.1.js"></script>
        ...
        ...
        ...省略
    </head>
    <body>
        ...
        ...
        ...省略
        <script type="text/javascript">
```

以下開始介紹script標籤中包含的部分。

1~5行使用了jQuery的 [$(document).ready](https://learn.jquery.com/using-jquery-core/document-ready/) 方法，保證在網頁加載完成後，才執行後面的function(){window.setInterval()}。

[window.setInterval()](https://developer.mozilla.org/zh-TW/docs/Web/API/Window/setInterval) 方法能夠重複執行任務，在這裡的設定是每300毫秒呼叫一次 key_autoload()。

第7行是使用jQuery選擇器取得前面的表單 "led_form" (如下)，並令他為一個名為frm的變數。
第8~18行，frm.[submit()](https://www.w3schools.com/jquery/event_submit.asp)則是監聽該表單提交時(使用者按下前面介紹的紫框中的submit按鈕)的行為。
8~18的內容我放在下面的補充資料，以免排版過長。
:::spoiler 補充資料
*我還是要聲明一下，我不是網頁相關從業人員，所以只能仰賴網路資料跟生成式AI提供的資料。*
第9行，preventDefault是為了避免提交時重新加載頁面(因為提交表單的按鈕有可能預設的行為包含重整頁面)。
緊接著在10~17行發出AJAX(Asynchronous JavaScript and XML，非同步處理，在不重新加載網頁的情況下與伺服器交流，我認為是因為這樣才會在前面加上preventDefault)，裡面的type、url使用attr取得led_form裡面的method跟action，向後端發送GET請求。
並且將表單的資料，轉為序列，送向後端，這可能是因為後端負責處裡的程式(剛剛action裡面的led.cgi)是以序列方式接收資料的。
![image](https://hackmd.io/_uploads/BJYj4YyzJx.png)

14~16行，當傳輸成功時(success)，將從伺服器取得led亮滅狀態(data)，動態更改ledimg_content(更換圖片)。
![image](https://hackmd.io/_uploads/BJUfmskMkl.png)


:::
![image](https://hackmd.io/_uploads/SkfKS6N-Je.png =360x150)

而20~29行的原理也是大同小異，基本上交互比對前面html表單元素與這邊的script就可以大致知道這裡的工作原理。


```javascript=
            $(document).ready(function() {
                window.setInterval(function() {
                    key_autoload();
                }, 300);
            }); //END $(document).ready()

            var frm = $('#led_form');
            frm.submit(function(e){
                e.preventDefault();
                    $.ajax({
                        type: frm.attr('method'),
                        url: frm.attr('action'),
                        data: frm.serialize(),
                        success: function(data){
                            $('#ledimg_content').html(data);
                        }
                    });
            });

            var key_frm = $('#key_form');
            function key_autoload(){
                $.ajax({
                    type: key_frm.attr('method'),
                    url: key_frm.attr('action'),
                    success: function(data){
                        $('#key_content').html(data);
                    }
                });
            }
```
以下是script的結束標籤，代表javascript腳本到此為止。
```html=
        </script>
    </body>
</html>
```
到這邊我們只剩下cgi的部分了。

### key_cgi.c

:::spoiler *展開完整原始碼*
```c=
 /* KEY CGI Program
 *
 *
 *
 * Note :
 *   PORT NAME[PIN] = GPIO [id]	
 *   PORTA[ 0]      = gpio[ 0x00]
 *   PORTA[ 1]      = gpio[ 0x01]	  
 *                  :
 *   PORTA[31]      = gpio[ 0x1F]
 *   PORTB[ 0]      = gpio[ 0x20]
 *                  :
 *   PORTB[31]      = gpio[ 0x3F]
 *                  :
 *                  :
 *                  :
 *   PORTI[ 0]      = gpio[ 0xC0]
 *                  :
 *                  :
 *   PORTI[31]      = gpio[ 0xDF]
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#if 0
//For NK-980IOT board
#define LED1_NUM	0x28 //PB8--->LED3
#define LED2_NUM	0x2D //PB13--->LED5
#endif

//For chili board
#define LED1_NUM	0x4B //PC11--->LED1
#define LED2_NUM	0x43 //PC3--->LED2

#if 0		
//For NuDesign-EtherU and NuDesign-EtherD
#define LED1_NUM	0x6C //PD12
#define LED2_NUM	0x6D //PD13
#define LED3_NUM	0x6E //PD14
#endif	

int main(void)
{
	FILE *fp;
	char str[256];

	char *data;
	long led1,led2;

	data = getenv("QUERY_STRING");

	if(data == NULL)
		printf("<P>Error! Error in passing data from form to script.");

	led1 = 0;
	led2 = 0;
	sscanf(data,"LED1=%ld&LED2=%ld",&led1,&led2);


	sprintf(str,"/sys/class/gpio/gpio%d/direction",LED1_NUM);
	if ((fp = fopen(str, "w")) == NULL) {

		//linux equivalent code "echo pin_num > export" to export the pin
		if ((fp = fopen("/sys/class/gpio/export", "w")) == NULL) {
			printf("Cannot open export file.\n");
			exit(1);
		}
		fprintf(fp, "%d", LED1_NUM);
		fclose(fp);

		//linux equivalent code "echo out > direction" to set the pin as an output  
		sprintf(str,"/sys/class/gpio/gpio%d/direction",LED1_NUM);
		if ((fp = fopen(str, "w")) != NULL) {
			fprintf(fp, "out");
			fclose(fp);
		}
	}
	else{
		fclose(fp);
	}

	sprintf(str,"/sys/class/gpio/gpio%d/direction",LED2_NUM);
	if ((fp = fopen(str, "w")) == NULL) {

		//linux equivalent code "echo pin_num > export" to export the pin
		if ((fp = fopen("/sys/class/gpio/export", "w")) == NULL) {
			printf("Cannot open export file.\n");
			exit(1);
		}
		fprintf(fp, "%d", LED2_NUM);
		fclose(fp);



		//linux equivalent code "echo out > direction" to set the pin as an output  
		sprintf(str,"/sys/class/gpio/gpio%d/direction",LED2_NUM);
		if ((fp = fopen(str, "w")) != NULL) {
			fprintf(fp, "out");
			fclose(fp);
		}
	}
	else{
		fclose(fp);
	}

	//linux equivalent code "echo "0" > value" to set the pin high/low 
	sprintf(str,"/sys/class/gpio/gpio%d/value",LED1_NUM);
	if ((fp = fopen(str, "w")) != NULL) {
		if(led1)
			fprintf(fp, "%d", 0);
		else
			fprintf(fp, "%d", 1);		
		fclose(fp);
	}
	
	//linux equivalent code "echo "0" > value" to set the pin high/low 
	sprintf(str,"/sys/class/gpio/gpio%d/value",LED2_NUM);
	if ((fp = fopen(str, "w")) != NULL) {
		if(led2)
			fprintf(fp, "%d", 0);
		else
			fprintf(fp, "%d", 1);		
		fclose(fp);
	}
	
	printf("<div id=\"ledimg_content\">\n");

	if((led1 == 0) && (led2 == 0)){	
		printf("<img id=\"led_img\" src=\"LED_1_off_2_off.jpg\">\n");
	}
	else if((led1 == 1) && (led2 == 0)){	
		printf("<img id=\"led_img\" src=\"LED_1_on_2_off.jpg\">\n");
	}
	else if((led1 == 0) && (led2 == 1)){	
		printf("<img id=\"led_img\" src=\"LED_1_off_2_on.jpg\">\n");
	}
	else if((led1 == 1) && (led2 == 1)){	
		printf("<img id=\"led_img\" src=\"LED_1_on_2_on.jpg\">\n");
	}

    printf("</drv>\n");

	return 0;
}
```
:::
這邊終於開始講C了！
最一開始是程式的功能說明及腳位地址轉換方式，通常C語言程式的開頭都會是程式的功能說明、授權相關文件等等。
關於C語言程式開頭、函數註解相對來說比較規矩的撰寫方式(或是說"生成方式")，請參考[Doxygen官方的Getting started](https://www.doxygen.nl/manual/starting.html)或是[@ichunlai撰寫的Doxygen入門](https://hackmd.io/@ichunlai/H1R9YD0B4)，Doxygen是一個能夠生成C專案說明文件的工具。

以這段註解(說明)來說，第1行「KEY CGI Program」，表明此程式就是負責處理按鈕狀態的程式。
```c=
 /* KEY CGI Program
 * Note :
 *   PORT NAME[PIN] = GPIO [id]	
 *   PORTA[ 0]      = gpio[ 0x00]
 *   PORTA[ 1]      = gpio[ 0x01]	  
 *                  :
 *   PORTA[31]      = gpio[ 0x1F]
 *   PORTB[ 0]      = gpio[ 0x20]
 *                  :
 *   PORTB[31]      = gpio[ 0x3F]
 *                  :
 *                  :
 *                  :
 *   PORTI[ 0]      = gpio[ 0xC0]
 *                  :
 *                  :
 *   PORTI[31]      = gpio[ 0xDF]
 */
```

```c=
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#if 0
//For NK-980IOT board
#define LED1_NUM	0x28 //PB8--->LED3
#define LED2_NUM	0x2D //PB13--->LED5
#endif

//For chili board
#define LED1_NUM	0x4B //PC11--->LED1
#define LED2_NUM	0x43 //PC3--->LED2

#if 0		
//For NuDesign-EtherU and NuDesign-EtherD
#define LED1_NUM	0x6C //PD12
#define LED2_NUM	0x6D //PD13
#define LED3_NUM	0x6E //PD14
#endif	
```

### led_cgi.c

:::spoiler *展開完整原始碼*
```c=
/* LED CGI Program
 *
 *
 * Note :
 *   PORT NAME[PIN] = GPIO [id]	
 *   PORTA[ 0]      = gpio[ 0x00]
 *   PORTA[ 1]      = gpio[ 0x01]	  
 *                  :
 *   PORTA[31]      = gpio[ 0x1F]
 *   PORTB[ 0]      = gpio[ 0x20]
 *                  :
 *   PORTB[31]      = gpio[ 0x3F]
 *                  :
 *                  :
 *                  :
 *   PORTI[ 0]      = gpio[ 0xC0]
 *                  :
 *                  :
 *   PORTI[31]      = gpio[ 0xDF]
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//For NK-980IOT board
//#define KEY1_NUM	0x8A //PE10
//#define KEY2_NUM	0x8C //PE12

//For chili board
#define KEY1_NUM	0x4F //PC15

int main(void)
{
	FILE *fp;
	char str[256];

	char buffer[10];
	int key1;

	key1 = 0;

	sprintf(str,"/sys/class/gpio/gpio%d/direction",KEY1_NUM);
	if ((fp = fopen(str, "w")) == NULL) {

		//linux equivalent code "echo pin_num > export" to export the pin
		if ((fp = fopen("/sys/class/gpio/export", "w")) == NULL) {
			printf("Cannot open export file.\n");
			exit(1);
		}
		fprintf(fp, "%d", KEY1_NUM);
		fclose(fp);

		//linux equivalent code "echo out > direction" to set the pin as an output  
		sprintf(str,"/sys/class/gpio/gpio%d/direction",KEY1_NUM);
		if ((fp = fopen(str, "w")) != NULL) {
			fprintf(fp, "in");
			fclose(fp);
		}
	}
	else{
		fclose(fp);
	}

	sprintf(str,"/sys/class/gpio/gpio%d/value",KEY1_NUM);
	if ((fp = fopen(str, "rb")) != NULL) {
		key1 = 0;
		fread(buffer, sizeof(char), sizeof(buffer) - 1, fp);
		key1 = atoi(buffer);
		fclose(fp);
	}

	printf("<div id=\"key_content\">\n");
	printf("<form id=\"key_form\" action=\"cgi-bin/key.cgi\" method=\"GET\">\n");
	printf("<table width=\"300\" border=\"1\">\n");
	printf("<tr>\n");
	printf("<td>KEY1</td>\n");
	printf("</tr>\n");
	printf("<tr>\n");
	printf("<td>\n");
	
	if(!key1){
		printf("<input type=\"radio\" id=\"KEY1\" name=\"KEY1\" value=\"1\" checked>ON <br>\n");
		printf("<input type=\"radio\" id=\"KEY1\" name=\"KEY1\" value=\"0\">OFF\n");
	}
	else{
		printf("<input type=\"radio\" id=\"KEY1\" name=\"KEY1\" value=\"1\">ON <br>\n");
		printf("<input type=\"radio\" id=\"KEY1\" name=\"KEY1\" value=\"0\" checked>OFF\n");
	}

	printf("</td>\n");
	printf("</tr>\n");
	printf("</table>\n");
	printf("<br>\n");
	printf("</form>\n");
	printf("</div>");

	return 0;
}
```
:::


















<!-- 

燒錄新唐開發板預設的uboot、uboot啟動參數(env.txt)、Image後，連接上網路線，並將電腦設置成與開發板(192.168.1.100)同網段，已經可以透過網頁成功點亮開發板LED並控制及接收按鈕狀態了。
但若是希望能開啟DHCP功能，就必須在console下達指令udhcpc，該如何透過shell script來執行開機自動化的dhcp呢？



# 透過腳本進行開機自動DHCP

## 腳本的撰寫

我在/mnt/mtdblock1中建立了bin資料夾用於存放後續的應用。
>`> mkdir bin`

先建立腳本 autodhcp.sh 用來執行udhcpc，詳細如下
![](https://hackmd.io/_uploads/rJZ5rFjI3.png)


```shell=+
#!/bin/sh
# Auto DHCP Client booting
# 2023/6/5 by maru
/sbin/udhcpc
echo -e "Auto DHCP Running"
exit 0
```

當我們執行這個腳本時，會實現啟動udhcpc，並print "Auto DHCP Running"

![](https://hackmd.io/_uploads/BkAgvtjIn.png)

可以看到，已經確實的取得ip位址了。

不相信可以再檢驗看看：
![](https://hackmd.io/_uploads/r1tSwYiIn.png)

## 開關機自動化執行(未完成)

現在要讓他能夠開機自動執行，必須在/etc/init.d另外寫個腳本，在開機時自動呼叫我們的autodhcp.sh，或許你會覺得這樣很多此一舉，但這樣預留了許多自動化的擴充性。
甚至我們可以將該腳本視為我們自定義的開關機流程腳本，獨立於系統的其他自動化腳本，我可以呼叫不只autodhcp.sh，甚至我之後可以自定義開機詢問帳號密碼、自動執行NTP之類的。

目前看來要在編譯前更改board/nuvoton/rootfs-chili/etc/init.d/rcS
結論是只有mnt/mtdblock1是可讀寫的空間，其他都是開機load進ram的。所以要在編譯階段就改rcS，開機自動執行mtdblock1中的某些腳本。
![image](https://hackmd.io/_uploads/S19L6Bezxl.png)




![](https://hackmd.io/_uploads/rJB_JqoI3.png)

```shell=+
#!/bin/sh
# Enviroment Setup
# 1.autodhcp
# 2023/6/5 by maru

umask 070

start(){
        echo -e "Enviroment Setup Starting"
        /mnt/mtdblock1/bin/autodhcp.sh
        echo -e "Enviroment Setup Done"
}

stop(){
}

restart(){
        stop
        start
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart|reload)
        restart
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?

```


## 硬體結構樹
![](https://hackmd.io/_uploads/SJwwieEvh.png)
這裡是以nuc980-dev-v1.0.dts為主

```
/*
 * Device Tree Source for NUC980 DEV board
 *
 * Copyright (C) 2018 Nuvoton Technology Corp.
 *
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */
/dts-v1/;
/include/ "nuc980.dtsi"

/ {
	model = "Nuvoton NUC980 DEV V1.0";
	compatible = "nuvoton,nuc980-dev-v1.0", "nuvoton,nuc980";

	chosen {
		bootargs = "root=/dev/ram0 console=ttyS0,115200n8 rdinit=/sbin/init mem=64M lpj=744448";
	};

	apb {
		uart1: serial@b0071000 {
			status = "disabled";
		};

		uart2: serial@b0072000 {
			status = "disabled";
		};

		uart3: serial@b0073000 {
			status = "disabled";
		};

		uart4: serial@b0074000 {
			status = "disabled";
		};

		uart5: serial@b0075000 {
			status = "disabled";
		};

		uart6: serial@b0076000 {
			status = "disabled";
		};

		uart7: serial@b0077000 {
			status = "disabled";
		};

		uart8: serial@b0078000 {
			status = "disabled";
		};

		uart9: serial@b0079000 {
			status = "disabled";
		};

		can0: can@b00a0000 {
			status = "disabled";
		};

		can1: can@b00a1000 {
			status = "disabled";
		};

		rtc: rtc@b0041000 {
			status = "disabled";
		};

		nadc: nadc@b0043000 {
			status = "disabled";
		};

		pwm0: pwm0@b0058000 {
			status = "disabled";
		};

		pwm1: pwm1@b0059000 {
			status = "disabled";
		};

		qspi0: qspi0@b0060000 {
			status = "disabled";
			#address-cells = <1>;
			#size-cells = <0>;

			num_cs  = <2>;
			lsb	 = <0>;
			txneg   = <1>;
			rxneg   = <0>;
			clkpol  = <0>;
			divider = <4>;
			sleep   = <0>;
			txbitlen = <8>;
			bus_num	= <0>;

			flash: m25p80@0 {
				compatible = "w25q128";
				#address-cells = <1>;
				#size-cells = <1>;
				reg = <0>;
				spi-max-frequency = <30000000>;

				partition@0 {
					label = "kernel";
					reg = <0x00000000 0x0800000>;
				};
				partition@1 {
					label = "rootfs";
					reg = <0x0800000 0x0800000>;
				};
			};
		};

		spi0: spi0@b0061000 {
			status = "disabled";
			#address-cells = <1>;
			#size-cells = <0>;

			num_cs  = <2>;
			lsb	 = <0>;
			txneg   = <1>;
			rxneg   = <0>;
			clkpol  = <0>;
			divider = <4>;
			sleep   = <0>;
			txnum   = <0>;
			txbitlen = <8>;
			bus_num	= <1>;

			spidev@0x01 {
				compatible = "spidev";
				spi-max-frequency = <30000000>;
				reg = <0>;
			};
		};

		spi1: spi1@b0062000 {
			status = "disabled";
			#address-cells = <1>;
			#size-cells = <0>;

			num_cs  = <2>;
			lsb	 = <0>;
			txneg   = <1>;
			rxneg   = <0>;
			clkpol  = <0>;
			divider = <4>;
			sleep   = <0>;
			txnum   = <0>;
			txbitlen = <8>;
			bus_num	= <2>;

			spidev@0x01 {
				compatible = "spidev";
				spi-max-frequency = <30000000>;
				reg = <0>;
			};
		};

		etimer0: etimer0@b0050000 {
			status = "disabled";
		};

		etimer1: etimer1@b0050100 {
			status = "disabled";
		};

		etimer2: etimer2@b0051000 {
			status = "disabled";
		};

		etimer3: etimer3@b0051100 {
			status = "disabled";
		};

		i2c0: i2c0@b0080000 {
			status = "disabled";

			nau8822: nau8822@1a {
				compatible = "nuvoton,nau8822";
				reg = <0x1a> ;
			};
		};

		i2c1: i2c1@b0081000 {
			status = "disabled";
			pinctrl-0 = <&pinctrl_i2c1_PB>;
			cap1_nt99141@2a {
                                compatible = "nuvoton,cap1-nt99141";
                                reg = <0x2a>;
                        };
		};


		i2c2: i2c2@b0082000 {
			status = "disabled";
			pinctrl-0 = <&pinctrl_i2c2_PB>;
			cap0_nt99141@2a {
                                compatible = "nuvoton,cap0-nt99141";
                                reg = <0x2a>;
                        };
		};

	};

	ahb {

		usbh_ehci@b0015000 {
			status = "okay";
		};
		usbh_ohci@b0017000{
			status = "okay";
		};

		usbdev@b0016000 {
			status = "disabled";
		};

		fmi@b0019000 {
			compatible = "nuvoton,nuc980-fmi", "nand";
			pinctrl-names = "default";
			pinctrl-0 = <&pinctrl_nand>;
			status = "disabled";
			#address-cells = <1>;
			#size-cells = <1>;

			partition@0x0 {
				label = "u-boot";
				reg = <0x00000000 0x0200000>;
			};

			partition@0x200000 {
				label = "Kernel";
				reg = <0x0200000 0x1400000>;
			};

			partition@0x1600000 {
				label = "user";
				reg = <0x1600000 0x6480000>;
			};
		};

		sdh@b0018000 {
			status = "disabled";
		};

		emac0@b0012000 {
			status = "okay";
		};
		emac1@b0022000 {
			status = "disabled";
		};
		cap0@b0024000 {
			model = "nt99141";
			status = "disabled";
		};
		cap1@b0014000 {
			model = "nt99141";
			status = "disabled";
		};
		dma@b0008000 {
			status = "okay";
		};

		i2s: i2s@b0020000 {
			status = "disabled";
		};

		i2s_pcm: i2s_pcm {
			status = "disabled";
		};

		sound {
			compatible = "nuvoton,nuc980-audio";
			i2s-controller = <&i2s>;
			i2s-platform = <&i2s_pcm>;
			status = "disabled";
		};
		ebi: ebi@b0010000 {
			status = "disabled";
		};
	};
};

```
:::info
```
chosen {
		bootargs = "root=/dev/ram0 console=ttyS0,115200n8 rdinit=/sbin/init mem=64M lpj=744448";
	};
```
**bootargs="root=/dev/ram0**
::: -->
