[toc]
## Test stand 影片介紹
[類似的1](https://www.youtube.com/watch?v=-yq1EmTkBCs&t=271s&pp=ygUXcm9ja2V0IG1vdG9yIHRlc3Qgc3RhbmQ%3D)
[類似的2](https://www.youtube.com/watch?v=LyU8Yo3oiW4&t=447s&pp=ygUXcm9ja2V0IG1vdG9yIHRlc3Qgc3RhbmQ%3D)
## Progress/ Difficulty
### 結構
訂單皆已送出 2023/7/22
完成組裝 2023/7/27
### PCB
PCB1 送出 2023/7/22
### Code
[Github](https://github.com/Herokroos/Product-design)
檔案建立 2023/7/22
### Integrated Test
[Video](https://youtu.be/qLWEzkJWCpg)
### Difficulty
2023/7/23
原本選的`STM32L010F4P6`的 **`Flash(16KB)`** 跟 **`RAM(2KB)`** 不夠,因此需要先把程式完成,查看所需大小再替換換新的MCU。後來決定使用STM32F103C8這款MCU。他有
## 組別
- 工程組
設計、製作test stand
- 公關組
IG帳號經營
shopee帳號建立(目前不確定)
與其他團體接洽(目前已知 ISP 屏中社團 台東高中生 建中航空社)
- 美宣組
製作相關介紹影片(以全英文為主)
產品介紹影片製作
Fusion 360 Animation + 實際測試影片
產品使用影片製作
Fusion 360 Animation + 操作步驟
## 發想
Amateur rocketeers要自己做出穩定的推力量測裝置實屬不容易,因此想開發一款可靠性高且平價的test stand
:::warning
:warning: [Warning, do not click this, Pls](https://youtu.be/bhh-pXDzqJA)
:::

## Requirements/ Limitations
設計一款test stand,適用於小型motor,具 **推力量測及資料紀錄功能**
推力範圍限制:最大不可超過10KG
## Specification
DAQ
- MCU
- AD-DC (HX711)
- SD Card connector
- Reset button
- Tare button
- Start button
- Power Supply Indicator
- SD Card Status Indicator
- Buzzer
## Architecture
### Flow chart

## Operating steps
|時間戳記| 實體操作 | MCU事件控制 | 蜂鳴器狀態指示 | LED指示燈 |
|:---:|:------------------------:|:----------------:|:--------------:|:---------:|
| | 硬體組裝 | --- | --- | --- |
| | 記憶卡插入 | --- | --- | --- |
| | 電池上電 | --- | --- | --- |
| | 系統測試(測試sd卡讀的到) | SD卡寫入測試訊息 | Pattern 0 |---|
| | motor置入 | Load cell測試 | --- | LED blinks |
| | 點火頭置入 | --- | --- | --- |
| | 扣重 | HX711讀值歸零 |--- | LED blinks and off |
|T-10 | 倒數計時開始 | Timer initialize | Pattern 1 | --- |
|T-0 | 點火 | MOSFET高電平/資料記錄開始| Pattern 2 | --- |
|T+2 | --- | MOSFET低電平 | --- | --- |
|T++ | --- | 資料寫入SD卡 | --- | --- |
|T+10 | --- | 資料記錄結束 | Pattern 3 | --- |
|T+20 | 系統結束操作 | --- | Pattern 4 | --- |
| | 電池移除 | --- | --- | --- |
| | 收拾 | --- | --- | --- |
## Component development
### Thrust range
> 10kg
### Load cell
> [10kg load cell](https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/TAL220M4M5Update.pdf?_gl=1*1rga2nj*_ga*MTA0NTA0NjkzOS4xNjg3NzkwNDcy*_ga_T369JS7J9N*MTY4ODg5ODYxNS4xLjAuMTY4ODg5ODYxNS4wLjAuMA..)
### Hardware Development
結構件:
>2020鋁型材 (3DPRT-MAKER創客專賣店)
>160mm 數量4
>木芯板 (木百貨)
>200 * 300 * 18 mm 數量1
3D列印件:
> 固定件 * 2
固定件:
>Hex Head Screws (3DPRT-MAKER創客專賣店)
>內六角 M5 * 40 數量8
>
>Phillips Screws/ Rounded Head Screws (商品不含運滿50元才出貨)
>十字 M4 * 6 數量2
>十字 M4 * 20 數量2
>十字 M5 * 30 數量4
>
>Socket Head Cap Screw (3DPRT-MAKER創客專賣店)
>內六角螺絲 M4 * 8 數量16
>
>T-slot Nut (3DPRT-MAKER創客專賣店)
>T型螺母 M4 數量16
>
>Hex Standoff, Male-Female
>六角黃銅螺柱 M4 * 10(內牙螺) * 6(外螺牙) 數量4
>
>---
>Spacer Support for PCB (商品不含運滿50元才出貨)
>六角黃銅螺柱 M3 * 10 數量4
>十字 M3 * 8 數量4
## DAQ Software&Hardware Configuration
### Private user code
```c
/* create a function in order to delay */
void delay_ms(uint32_t target) {
__HAL_TIM_SET_COUNTER(&htim2, 0);
while(__HAL_TIM_GET_COUNTER(&htim2) < target) {
// waiting counter to count to target
}
}
/* create a function in order indicate the stauts of the loadcell */
void LC_func(uint32_t times){
for (int i =0;i<= times;i++ ){
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);// high level
delay_ms(200);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);// low level
delay_ms(200);
}
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);// low level
delay_ms(1800);
}
```
### Timer
#### Timer Configuration
:::info
:bulb:
Using Timer2; APB1 timer clocks
記數單位設定為1000ms

Timer clock frequency(Hz) = 8MHz
Prescaler = 8000 - 1
Counter Period = 10000
8MHz/8000 * 1000 = 1Hz = 1000ms
:::
#### Basic code
```c
/* create a function to count time*/
void delay_ms(int target) {
__HAL_TIM_SET_COUNTER(&htim2, 0);
HAL_TIM_Base_Start(&htim2);
while(__HAL_TIM_GET_COUNTER(&htim2) < target) {
// waiting counter to count to target
}
}
/* init the TIM */
static void MX_TIM2_Init(void){
/* USER CODE BEGIN TIM2_Init 2 */
HAL_TIM_Base_Start(&htim2); // start the TIM
/* USER CODE END TIM2_Init 2 */
}
```
---
### printf
```c
/* USER CODE BEGIN 4*/
int __io_putchar(int ch) {
ITM_SendChar(ch);
return ch;
}
```
---
### LED
#### GPIO Configuration
:::info
:bulb:
GPIO **`GPIO_Output`**
GPIO output level **`Low`**
GPIO mode **`Output Push Pull`**
GPIO Pull-up/ Pull-down **`No push-up and no pull-down`**
Maximum output speed **`Low`**
User Lable **`LED`**
:::
#### Basic Code
```c
/* in main.h */
#define LED_Pin GPIO_PIN_2
#define LED_GPIO_Port GPIOA
/* in main.c */
while(1){
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_PIN);// 翻轉狀態
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);// high level
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);// low level
}
```
### Buzzer
#### GPIO Configuration
:::info
:bulb:
GPIO **`GPIO_Output`**
GPIO output level **`Low`**
GPIO mode **`Output Push Pull`**
GPIO Pull-up/ Pull-down **`No push-up and no pull-down`**
Maximum output speed **`Low`**
User Lable **`Buzzer`**
:::
| 時間戳記 | Pattern | High level |Low level|extra|
|:-------------------------------:|:-------:|:----------:|:------:|:---:|
| When the SD Card can't be read | 0 | 400ms | 200ms |High level 400ms|
| At T-10(Start button is pressed) | 1 | 500ms | 500ms |10times|
| At T-0 | 2 | 1000ms | 9000ms| |
| At T=10 | 3 | 200ms | 800ms | |
| At T=20 | 4 | 2000ms | | |
#### Code
```c
/* in main.h */
#define Buzzer_Pin GPIO_PIN_0
#define Buzzer_GPIO_Port GPIOB
/* in main.c */
while(1){
/* pattern 0 */
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(400);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_RESET);// low level
delay_ms(200);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(400);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// low level
/* pattern 1 */
for(int p1 = 0; p1 < 10; p1++){// repeats 10 times
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(500);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_RESET);// low level
delay_ms(500);
}
/* pattern 2 */
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(1000);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_RESET);// low level
delay_ms(9000);
/* pattern 3 */
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(200);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_RESET);// low level
delay_ms(800);
/* pattern 4 */
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_SET);// high level
delay_ms(2000);
HAL_GPIO_WritePin(Buzzer_GPIO_Port, Buzzer_Pin, GPIO_PIN_RESET);// low level
}
```
---
### Button
#### Configuration
:::info
:bulb:
1. **Tare button**
GPIO **`GPIO_Input`**
GPIO mode **`Input mode`**
GPIO Pull-up/ Pull-down **`No push-up and no pull-down`**
User Lable **`Tare`**
2. **Start button**
GPIO **`GPIO_Input`**
GPIO mode **`Input mode`**
GPIO Pull-up/ Pull-down **`No push-up and no pull-down`**
User Lable **`SWpin`**
:::
#### Code
```c
/* in main.h */
#define Tare_Pin GPIO_PIN_10 //tare button
#define Tare_GPIO_Port GPIOA
#define SWpin_Pin GPIO_PIN_1 // start button
#define SWpin_GPIO_Port GPIOA
/* in main.c */
/* USER CODE BEGIN PV */
int Tare_button_state = 0;
int Start_button_state = 0;
/* USER CODE END PV */
while(1){
Tare_button_state = HAL_GPIO_ReadPin(Tare_GPIO_Port, Tare_Pin);
if(Tare_button_state == GPIO_PIN_RESET){// low level
//tare
}
Start_button_state = HAL_GPIO_ReadPin(SWpin_GPIO_Port,SWpin_Pin);
if(Start_button_state == GPIO_PIN_RESET){// low level
//sequence start
}
}
```
---
### SD CARD
#### Configuration
:::info
:bulb: **SD CARD configuration**
1. Set the SD CARD `format` to **FAT32**, `allocation unit size` **4096**

if the allocation unit size does not have the option `4096`, you may follow steps below to change it
- Win+R, enter `diskmgmt.msc`
- Select and right click the micro sd card, click the **Format**
- Select the `file system` **FAT32**, `allocation unit size` **4096**
- Apply
:::
:::info
1. SPI1 Mode **`Full duplex Master`**
2. select one pin
GPIO **`GPIO_Output`**
GPIO mode **`Push-Pull`**
User_Lable: **`SD_CS`**
3. **MIddleware** >>FAT32
--> User define
--> Enable **`USE_LFN`** with `Enabled with static working buffer on the BSS`
--> Set **`MAX_SS`** `4096`
--> Generate Code
4. Include **Library** into STM32CUBEIDE
[fatfs_sd.h](https://raw.githubusercontent.com/eziya/STM32_SPI_SDCARD/master/Old_STM32F1/Inc/fatfs_sd.h)
[fatfs_sd.c](https://raw.githubusercontent.com/eziya/STM32_SPI_SDCARD/master/Old_STM32F1/Src/fatfs_sd.c)
6. **Modifications**
Modify the File
**`stm32l0xx.it.c`**
**`user_diskio.c`**
See the code at [here](https://embetronicx.com/tutorials/microcontrollers/stm32/stm32-sd-card-interfacing-with-example/)
:::
#### Code in main.c
```c
#include "fatfs.h"
#include "fatfs_sd_card.h"
#include "string.h"
#include "stdio.h"
#include "stdbool.h"
float Thrust;
int main(void){
process_SD_card();
while(1){
...
/* SD writing */
fres = f_mount(&FatFs, "", 1); //1=mount now
if (fres != FR_OK)
{
printf("No SD Card found : (%i)\r\n", fres);
break;
}
printf("SD Card Mounted Successfully!!! data coming in!!!\r\n");
fres = f_open(&fil, "data.txt", FA_WRITE | FA_READ | FA_CREATE_ALWAYS);
if(fres != FR_OK)
{
printf("File creation/open Error : (%i)\r\n", fres);
break;
}
//write the data
printf("Writing data!!!\r\n");
f_puts("Confirmation SD card open successfully", &fil);
//close your file
f_close(&fil);
printf("Closing File!!!\r\n");
/* unmount SD card*/
f_mount(NULL, "", 0);
printf("SD Card Unmounted Successfully!!!\r\n");
}
}
void process_SD_card( void ){
FATFS FatFs; //Fatfs handle
FIL fil; //File handle
FRESULT fres; //Result after operations
char buf[100];
do
{
//Mount the SD Card
fres = f_mount(&FatFs, "", 1); //1=mount now
if (fres != FR_OK)
{
printf("No SD Card found : (%i)\r\n", fres);
break;
}
printf("SD Card Mounted Successfully!!!\r\n");
//Read the SD Card Total size and Free Size
FATFS *pfs;
DWORD fre_clust;
uint32_t totalSpace, freeSpace;
f_getfree("", &fre_clust, &pfs);
totalSpace = (uint32_t)((pfs->n_fatent - 2) * pfs->csize * 0.5);
freeSpace = (uint32_t)(fre_clust * pfs->csize * 0.5);
printf("TotalSpace : %lu bytes, FreeSpace = %lu bytes\n", totalSpace, freeSpace);
//Open the file
fres = f_open(&fil, "data.txt", FA_WRITE | FA_READ | FA_CREATE_ALWAYS);
if(fres != FR_OK)
{
printf("File creation/open Error : (%i)\r\n", fres);
break;
}
printf("Writing data!!!\r\n");
//write the data
f_puts("Confirmation SD card open successfully", &fil);
//close your file
f_close(&fil);
printf("Closing File!!!\r\n");
} while( false );
//We're done, so de-mount the drive
f_mount(NULL, "", 0);
printf("SD Card Unmounted Successfully!!!\r\n");
}
```
#### Reference
[Reference](https://embetronicx.com/tutorials/microcontrollers/stm32/stm32-sd-card-interfacing-with-example/)
[FAT32 functions](http://elm-chan.org/fsw/ff/00index_e.html)
---
### HX711
[Github](https://github.com/PCov3r/HX711-STM32-Library/tree/main)
[flaot to string](https://www.796t.com/content/1541228343.html)
[enable the printf in stm32CubeIDE](https://blog.csdn.net/sudaroot/article/details/105099807)
#### Configuration
:::info
:bulb:q
1. GPIO **`GPIO_Output`**
GPIO mode **`No push-up and no pull-down`**
User_Lable: **`PD-SCK`**
2. GPIO **`GPIO_Input`**
GPIO mode **`No push-up and no pull-down`**
User_Lable: **`DAT`**
:::
#### Calibration
:::info
:bulb:
calibrate the coef by adjusting the x in
**`hx711_coef_set(&loadcell, x);`**
that **hx711_weight** = 0
:::
```C
/* in main.c*/
#include "hx711.h"
hx711_t loadcell;
float Thrust;
.
.
int main()
{
hx711_init(&loadcell, PD-SCK_GPIO_Port, PD-SCK_Pin, DAT_GPIO_Port, DAT_Pin);
hx711_coef_set(&loadcell, x); // read afer calibration, x varies with different loadcell
hx711_tare(&loadcell, 10); // tare the value
while (1)
{
HAL_Delay(500);
Thrust = hx711_weight(&loadcell, 10);
}
}
```
#### Code
```C
/* in hx711.c*/
/* add code below before hx711_delay_us */
static inline void __nop(void)
{
__asm__ __volatile__("nop");
}
/* in main.c*/
#include "hx711.h"
hx711_t loadcell;
float Thrust;
.
.
int main()
{
hx711_init(&loadcell, PD-SCK_GPIO_Port, PD-SCK_Pin, DAT_GPIO_Port, DAT_Pin);
hx711_coef_set(&loadcell, x); // read afer calibration, x varies with different loadcell
hx711_tare(&loadcell, 10); // tare the value
while (1)
{
/* use it only when adjusting the coef
HAL_Delay(500);
*/
Thrust = hx711_weight(&loadcell, 10);
}
}
```
## PCB Design
PCB (DAQ)
~~STM32L010F4P6~~
[STM32F103C8T6 datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf)
[STM32F103C8T6 schematic reference1](https://stm32-base.org/assets/pdf/boards/original-schematic-STM32F103C8T6-Blue_Pill.pdf)
[STM32F103C8T6 schematic reference2](https://kicad-info.s3.dualstack.us-west-2.amazonaws.com/original/3X/e/4/e4c88b48af7bf832b11c87d979695b311abeaeb4.svg)
[STM32F103C8T6 schematic reference3](https://i.stack.imgur.com/db32S.png)
[HX711 datasheet](https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf)
[HX711 schematic](https://image.easyeda.com/components/5a7e011054ba4e0d9779a664469bc3ab.png)
[SD CARD schematic](https://i.stack.imgur.com/mRfRK.jpg)
[Voltage Regulator](https://img-blog.csdnimg.cn/20190225095242384.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1hMX19NQVg=,size_16,color_FFFFFF,t_70)
[CH340 schematic reference(web)](http://nicecircuits.com/ch340g-usb-to-rs232-ttl-module-schematic-d-sun-v3-0/)
[CH340 schematic reference(github)](https://github.com/NiceCircuits/modules_from_china/tree/master/CH340G_D-SUN-V3.0)
## Debug
[terminal](http://www.embedded-communication.com/en/misc/printf-with-st-link/)
[float printf](https://forum.digikey.com/t/easily-use-printf-on-stm32/20157)
## System integration test
測試自製motor
## 已知支出 / 產品內容物
| 品項名稱 | 數量 | 價錢 | 小計 |
|:----------------:|:----:|:----:|:----:|
| 18mm 木芯板 | 1 | 71 | 71 |
| 2020鋁型材 | 4 | 62 | 248 |
| 3D列印件(固定板) | 2 | | |
| M4 T型螺母 | 16 | 3 | 48 |
| M4*8內六角螺絲 | 16 | 2 | 32 |
| M5*40內六角螺絲 | 8 | 5 | 40 |
| M4*6十字螺絲 | 2 | 1 | 2 |
| M4*20十字螺絲 | 2 | 2 | 4 |
| M5*30十字螺絲 | 4 | 3 | 12 |
| M4六角黃銅螺柱 | 4 | 3 | 12 |
| M3六角黃銅螺柱 | 4 | | |
| M3*6十字螺絲 | 4 | | |
| PCB | 1 | | |
| Load cell | 1 | | |
## 未來規劃
短期目標(9月前): 功能測試
中期目標(學測前): 產品試用
最終目標(學測前): 進行販售