# ThreadX demo
## 晶圓廠自動化運輸模擬
#### OHT: 空中走行式無人搬運車
#### Load Port: 晶圓裝卸機
#### Foup: 晶圓傳送盒
#### Sensor*2: Present sensor and Placement sensor
```mermaid
flowchart TB
%% 等待 Sensor 觸發部分
subgraph "等待Sensor觸發"
direction TB
LP1["Load Port 1\n(Sensor1 & Sensor2)"]
LP2["Load Port 2\n(Sensor1 & Sensor2)"]
LP3["Load Port 3\n(Sensor1 & Sensor2)"]
LP4["Load Port 4\n(Sensor1 & Sensor2)"]
LP5["Load Port 5\n(Sensor1 & Sensor2)"]
end
%% 將所有 LP 指向 CheckLimit
LP1 --> CheckLimit["<b>兩個Sensor同時觸發</b>\n判定LP上有Foup\n請求OHT傳送"]
LP2 --> CheckLimit
LP3 --> CheckLimit
LP4 --> CheckLimit
LP5 --> CheckLimit
%% CheckLimit 的結果處理
CheckLimit -->|允許| OHTRequest["發送 OHT 傳送請求"]
CheckLimit -->|請求達到上限三台| WaitQueue["加入等待隊列"]
%% 循環等待
WaitQueue --> CheckLimit
%% 結束傳送
OHTRequest --> Complete["傳送完成"]
Complete --> Reset["返回等待 Sensor 觸發"]
%% 備註部分
Note1["<b>備註:</b> 系統最多允許3台 Load Port 同時請求 OHT 傳送"]
Note1 -.- CheckLimit
```
## ThreadX API Services used
###
###
#### thread
tx_thread_create
#### event flags (LP Sensor狀態)
tx_event_flags_create
tx_event_flags_set
tx_event_flags_get
#### semaphore (控管OHT使用量)
tx_semaphore_create
tx_semaphore_get
tx_semaphore_put
#### queue (Foup傳輸數量計數)
tx_queue_create
tx_queue_send
tx_queue_receive
#### timer (製造Sensor假訊號)
tx_timer_create
---
### event flags使用(LP Sensor狀態)

#### app_azure_rtos.c
```c=
#include "app_azure_rtos.h"
#define THREAD_STACK_SIZE 1024
#define THREAD_COUNT 10
#define QUEUE_SIZE 10
uint8_t thread_stack[THREAD_COUNT][THREAD_STACK_SIZE];
ULONG queue_storage[QUEUE_SIZE];
TX_THREAD thread_0, thread_1, thread_2, thread_3, thread_4, thread_5, thread_6,
thread_7, thread_8, thread_9, counter_thread;
VOID time_display(ULONG initial_input);
VOID LoadPort_1(ULONG initial_input);
VOID LoadPort_2(ULONG initial_input);
VOID LoadPort_3(ULONG initial_input);
VOID LoadPort_4(ULONG initial_input);
VOID LoadPort_5(ULONG initial_input);
VOID timer_callback(ULONG input);
VOID lpCounter(ULONG initial_input);
TX_EVENT_FLAGS_GROUP loadportST;
TX_SEMAPHORE vehicleST;
TX_TIMER sensorSignal;
TX_QUEUE counter_queue;
VOID tx_application_define(VOID *first_unused_memory) {
tx_thread_create(&thread_6, "Time Display", time_display, 0,
thread_stack[6], THREAD_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE,
TX_AUTO_START);
tx_thread_create(&thread_0, "LoadPort_1", LoadPort_1, 0, thread_stack[0],
THREAD_STACK_SIZE, 5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);
tx_thread_create(&thread_1, "LoadPort_2", LoadPort_2, 0, thread_stack[1],
THREAD_STACK_SIZE, 7, 7, 5, TX_AUTO_START);
tx_thread_create(&thread_2, "LoadPort_3", LoadPort_3, 0, thread_stack[2],
THREAD_STACK_SIZE, 9, 9, 5, TX_AUTO_START);
tx_thread_create(&thread_3, "LoadPort_4", LoadPort_4, 0, thread_stack[3],
THREAD_STACK_SIZE, 11, 11, 5, TX_AUTO_START);
tx_thread_create(&thread_4, "LoadPort_5", LoadPort_5, 0, thread_stack[4],
THREAD_STACK_SIZE, 13, 13, 5, TX_AUTO_START);
tx_event_flags_create(&loadportST, "loadportST");
tx_semaphore_create(&vehicleST, "vehicleST", 3);
tx_timer_create(&sensorSignal, "sensorSignal", timer_callback, 0, 1,
TX_TIMER_TICKS_PER_SECOND, TX_AUTO_ACTIVATE);
tx_queue_create(&counter_queue, "Counter Queue", TX_1_ULONG,
queue_storage, QUEUE_SIZE * sizeof(VOID *));
tx_thread_create(&counter_thread, "lpCounter", lpCounter, 0,
thread_stack[5], THREAD_STACK_SIZE, 15, 15, TX_NO_TIME_SLICE,
TX_AUTO_START);
}
```
#### app_threadx.c
```c=
#include "app_threadx.h"
#include "tx_api.h"
#include <stdio.h>
#include "main.h"
#include "stm32f4xx_hal_rtc.h"
extern TX_EVENT_FLAGS_GROUP loadportST;
extern TX_SEMAPHORE vehicleST;
extern TX_QUEUE counter_queue;
extern RTC_HandleTypeDef hrtc;
UINT pr = 0, pl = 0;
UINT lp_counters[5] = { 0 };
VOID time_display(ULONG initial_input) {
while (1) {
RTC_TimeTypeDef sTime = { 0 };
RTC_DateTypeDef sDate = { 0 };
// 獲取目前時間
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
// 獲取目前日期
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// 顯示時間和日期
printf("------------------\n");
printf("Time: %02d:%02d:%02d\n", sTime.Hours, sTime.Minutes,
sTime.Seconds);
tx_thread_sleep(100); // 休眠 100 ticks
}
}
VOID LoadPort_1(ULONG initial_input) {
ULONG current_flags; // 用於存放事件標誌當前值
while (1) {
// 檢測條件,設置事件標誌
if (pr == 1) {
tx_event_flags_set(&loadportST, 0x01, TX_OR); // 設置事件標誌 bit 0
}
if (pl == 3) {
tx_event_flags_set(&loadportST, 0x02, TX_OR); // 設置事件標誌 bit 1
}
// 檢查事件標誌是否滿足條件
if (tx_event_flags_get(&loadportST, 0x03, TX_AND_CLEAR, ¤t_flags,
TX_NO_WAIT) == TX_SUCCESS) {
printf("LP1 requesting\n");
// 獲取車輛資源信號量
tx_semaphore_get(&vehicleST, TX_WAIT_FOREVER);
printf("LP1 request success\n");
// 模擬處理時間
tx_thread_sleep(600);
printf("OHT takes Cassette from LP1\n");
UINT lp_ct = 0; // LP1 的索引
// 將 LP1 的索引送入隊列
tx_queue_send(&counter_queue, &lp_ct, TX_NO_WAIT);
// 釋放車輛資源信號量
tx_semaphore_put(&vehicleST);
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID LoadPort_2(ULONG initial_input) {
ULONG current_flags; // 用於存放事件標誌當前值
while (1) {
// 檢測條件,設置事件標誌
if (pr == 2) {
tx_event_flags_set(&loadportST, 0x04, TX_OR); // 設置事件標誌 bit 2
}
if (pl == 4) {
tx_event_flags_set(&loadportST, 0x08, TX_OR); // 設置事件標誌 bit 3
}
// 檢查事件標誌是否滿足條件
if (tx_event_flags_get(&loadportST, 0x0c, TX_AND_CLEAR, ¤t_flags,
TX_NO_WAIT) == TX_SUCCESS) {
printf("LP2 requesting\n");
// 獲取車輛資源信號量
tx_semaphore_get(&vehicleST, TX_WAIT_FOREVER);
printf("LP2 request success\n");
// 模擬處理時間
tx_thread_sleep(600);
printf("OHT takes Cassette from LP2\n");
UINT lp_ct = 1; // LP2 的索引
// 將 LP2 的索引送入隊列
tx_queue_send(&counter_queue, &lp_ct, TX_NO_WAIT);
// 釋放車輛資源信號量
tx_semaphore_put(&vehicleST);
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID LoadPort_3(ULONG initial_input) {
ULONG current_flags; // 用於存放事件標誌當前值
while (1) {
// 檢測條件,設置事件標誌
if (pr == 3) {
tx_event_flags_set(&loadportST, 0x10, TX_OR); // 設置事件標誌 bit 4
}
if (pl == 5) {
tx_event_flags_set(&loadportST, 0x20, TX_OR); // 設置事件標誌 bit 5
}
// 檢查事件標誌是否滿足條件
if (tx_event_flags_get(&loadportST, 0x30, TX_AND_CLEAR, ¤t_flags,
TX_NO_WAIT) == TX_SUCCESS) {
printf("LP3 requesting\n");
// 獲取車輛資源信號量
tx_semaphore_get(&vehicleST, TX_WAIT_FOREVER);
printf("LP3 request success\n");
// 模擬處理時間
tx_thread_sleep(600);
printf("OHT takes Cassette from LP3\n");
UINT lp_ct = 2; // LP3 的索引
// 將 LP3 的索引送入隊列
tx_queue_send(&counter_queue, &lp_ct, TX_NO_WAIT);
// 釋放車輛資源信號量
tx_semaphore_put(&vehicleST);
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID LoadPort_4(ULONG initial_input) {
ULONG current_flags; // 用於存放事件標誌當前值
while (1) {
// 檢測條件,設置事件標誌
if (pr == 4) {
tx_event_flags_set(&loadportST, 0x40, TX_OR); // 設置事件標誌 bit 6
}
if (pl == 6) {
tx_event_flags_set(&loadportST, 0x80, TX_OR); // 設置事件標誌 bit 7
}
// 檢查事件標誌是否滿足條件
if (tx_event_flags_get(&loadportST, 0xc0, TX_AND_CLEAR, ¤t_flags,
TX_NO_WAIT) == TX_SUCCESS) {
printf("LP4 requesting\n");
// 獲取車輛資源信號量
tx_semaphore_get(&vehicleST, TX_WAIT_FOREVER);
printf("LP4 request success\n");
// 模擬處理時間
tx_thread_sleep(600);
printf("OHT takes Cassette from LP4\n");
UINT lp_ct = 3; // LP4 的索引
// 將 LP4 的索引送入隊列
tx_queue_send(&counter_queue, &lp_ct, TX_NO_WAIT);
// 釋放車輛資源信號量
tx_semaphore_put(&vehicleST);
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID LoadPort_5(ULONG initial_input) {
ULONG current_flags; // 用於存放事件標誌當前值
while (1) {
// 檢測條件,設置事件標誌
if (pr == 5) {
tx_event_flags_set(&loadportST, 0x100, TX_OR); // 設置事件標誌 bit 8
}
if (pl == 7) {
tx_event_flags_set(&loadportST, 0x200, TX_OR); // 設置事件標誌 bit 9
}
// 檢查事件標誌是否滿足條件
if (tx_event_flags_get(&loadportST, 0x300, TX_AND_CLEAR, ¤t_flags,
TX_NO_WAIT) == TX_SUCCESS) {
printf("LP5 requesting\n");
// 獲取車輛資源信號量
tx_semaphore_get(&vehicleST, TX_WAIT_FOREVER);
printf("LP5 request success\n");
// 模擬處理時間
tx_thread_sleep(600);
printf("OHT takes Cassette from LP5\n");
UINT lp_ct = 4; // LP5 的索引
// 將 LP5 的索引送入隊列
tx_queue_send(&counter_queue, &lp_ct, TX_NO_WAIT);
// 釋放車輛資源信號量
tx_semaphore_put(&vehicleST);
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID lpCounter(ULONG initial_input) {
UINT lp_ct; // 用於接收隊列中的 LP 索引
while (1) {
// 從隊列中接收 LP 索引
if (tx_queue_receive(&counter_queue, &lp_ct, TX_NO_WAIT) == TX_SUCCESS) {
lp_counters[lp_ct]++; // 更新對應 LP 的計數器
}
// 如果按鈕被按下,顯示所有 LP 的計數器值
if (HAL_GPIO_ReadPin(GPIOD, PB_0_Pin) == GPIO_PIN_SET) {
printf("All LP counters: ");
for (UINT i = 0; i < 5; i++) {
printf("LP%u: %u ", i + 1, lp_counters[i]);
tx_thread_sleep(2); // 避免顯示過快
}
printf("\n");
}
if (HAL_GPIO_ReadPin(GPIOD, PB_1_Pin) == GPIO_PIN_SET){
memset(lp_counters, 0, sizeof(lp_counters));
}
tx_thread_sleep(10); // 休眠 10 ticks
}
}
VOID timer_callback(ULONG input) {
pr++; // 遞增 pr 值
pl++; // 遞增 pl 值
if (pr == 8) {
pr = 1; // 重置 pr 為 1
pl = 1; // 重置 pl 為 1
}
}
```