# 國立臺灣大學賽車隊軟體研發日誌-BMS/MUX_ADC_NTC
##### 組別: 動力組
##### 負責人: 李星辰
##### 次系統: BMS
##### 次系統編號: *填入次系統編號*
##### 軟體名稱: MUX_ADC_NTC
##### 軟體編號: *填入軟體編號*
##### 存放位置: *加入軟體存放位置超連結*
##### 開始設計日期: 2022/07/22
##### 版本: 1.0
##### 更動時間: 2022/07/29
##### 組長檢核: *填入組長檢核者*
##### 組長檢核日期: *填入組長檢核日期*
##### 技術長檢核: *填入技術長檢核*
##### 技術長檢核日期: *填入技術長檢核日期*
---
## 完整工程目標
> 監測電池各個 Segment 電極處的溫度並回傳值給 Master Board
## 程式架構
> 執行順序: MUX選擇通訊通道 → ADC讀電壓值 → 計算電阻 → 回傳溫度
## 使用的函式庫
> [Arduino.h & math.h](https://www.arduino.cc/reference/en/)
> [Wire.h](https://www.arduino.cc/reference/en/language/functions/communication/wire/)
> [ADS1x15.h by RobTillaart](https://github.com/RobTillaart/ADS1X15)
## 演算法參考資料
> [TCA9548A I2C Multiplexer Module](https://www.instructables.com/TCA9548A-I2C-Multiplexer-Module-With-Arduino-and-N/)
> [Thermistors and NTC Thermistors](https://www.electronics-tutorials.ws/io/thermistors.html)
> [NTC Thermistor Beta](https://www.ametherm.com/thermistor/ntc-thermistor-beta)
> [Calculating Temperature from Resistance](https://www.northstarsensors.com/calculating-temperature-from-resistance)
## 測試環境
> 填入測試環境(有用到的軟體以及版本)
##### 電腦硬體:ROG ZEPHYRUS GA401
##### 作業系統: Windows 10 家用版 21H2
##### 編譯器版本(python版本): Arduino IDE 1.8.15
##### 其他硬體
> MCU: Arduino Nano
> MUX: TCA9548A I2C MUX
> ADC: Ads1115
> NTC: NXFT15XH103FA2B100
> [NTC DataSheet](https://www.mouser.tw/datasheet/2/281/1/NXFT15XH103FA2B100-2936858.pdf)
---
## 2.0板內容
- 各元件API整理/編寫
#### MUX
我們選的MUX( TCA9548A )在使用上非常簡單,因此只需要包含ADDRESS以及CHANNEL就可以了。
```cpp=
// 宣告MUX類別
class MUX {
public:
// __init__
MUX()
: _ADDR(0x70), _Channel(0) { //預設位址、預設通道
set_channel();
}
MUX(uint8_t ADDR)
: _ADDR(ADDR), _Channel(0) { //有更改位址、預設通道
set_channel();
}
MUX(uint8_t ADDR, int CH)
: _ADDR(ADDR), _Channel(CH) { //有更改位置、有其他預設通道
set_channel();
}
// public var
// func
//啟動MUX
bool begin();
bool begin(uint8_t ADDR);
//設置通道
void set_channel();
void set_channel(uint8_t CH);
//回傳是否啟動
bool operator bool (){
return _Active;
}
private:
bool _Active = false;
uint8_t _ADDR;
uint8_t _Channel;
};
```
#### NTC
將所有跟熱敏電阻有關的變數全部提取+整理。
```cpp=
//宣告NTC物件
class NTC {
public:
// init
// default Resistor: 10k, Temp: 25C
NTC()
: _R(10000), _T(25), _val(25), _V(2.5), _ratio(0.1) {}
NTC(double R)
: _R(R), _V(2.5), _val(0), _ratio(0) { R2T(); }
// set vary type of var
void set_ratio(double ratio); // Use for val->V
void set_val(int16_t val); // Set value
void set_R(double R); // Set resistance
void set_V(double V); // Set Voltage
void set_T(double T); // Set Temp
//Transform value to Voltage
void val2V();
void val2V(double ratio);
// transform Resistor to Temp
void R2T();
void R2T(char* a);
// transform Voltage to Resistor
void V2R();
void V2R(double Vref);
void V2R(double Vref, double RX);
// get each datas
int16_t get_val() { return _val; }
double get_Resistor() { return _R; }
double get_Temp() { return _T; }
double get_Voltage() { return _V; }
void show();
private:
double _ratio; // val to V ratio
int16_t _val; // value
double _R; // Resistance
double _V; // Voltage
double _T; // Temp
};
```
#### Timer
因為會需要定時回傳/檢查數據,乾脆直接把定時器寫成一個簡單的物件,方便後續的定時。
```cpp=
class Timer {
public:
//init
Timer()
: _start_time(0), _current_time(0), _time_gap(1000), _difference(0) {}
Timer(long int gap)
: _start_time(0), _current_time(0), _time_gap(gap), _difference(0) {}
void begin(); // 開始計時
void set_time_gap( long int g ); //設定定時間格
bool check_time(); //確認計時
long int get(); //回傳經過秒數
private:
long int _start_time;
long int _current_time;
long int _difference;
long int _time_gap;
};
```
## 2.0版測試結果
尚未測試,但編譯器有過 + 是拿原本程式碼改的,問題應該不大。
會在測試後補上結果
## 2.0版改進方向
> ADC整理: 他有夠難設定
> 希望目標: 可以依照Segment回傳,設定電壓輸入參考腳位
> ADC函式庫看要不要改成 Adafruit 的
---
## 1.0 版內容
- MUX 多工器 選擇/切換通訊通道
- ADC 類比/數位轉換器 一對四
- 熱敏電阻/溫度轉換函式
#### MUX
##### 用途
MUX(數據多工器)用於減少讀取數值所需要的腳位數量,要讀取n個輸入,只需要$log_2(n)$個腳位。本次使用的MUX使用I2C通訊界面來處理不同訊號輸入。I2C MUX通常用於避免擁有相同I2C位址的芯片互相衝突。
##### 使用原因
在EP4使用的電池,會需要處理8組電池 Segment,每組電池需要量測12個點,總共96個量測點,同時電池Segment之間會需要做隔離。ver 1.0採用的ADC每顆可以轉換4個不同輸入源,同時可以設定四組不同的位址,在沒有使用MUX的情況下最多可以量測16比不同數據,遠遠不到96組,同時又要滿足隔離的條件,因此需要8通道的MUX。
##### 接線

TCA9548A的腳位配置非常簡單,SDA與SCL分別對應Master Board的SDA/SCL腳位,A0~A2用來設置MUX的通訊位址,剩餘的腳位用來接上要通訊的芯片就接好了

##### 使用方式
TCA9548A的使用方式非常簡單,只需要將要通訊的通道寫道MUX裡面即可。
ex: 想要與Channel 0通訊,只要寫入 B00000001 (0x01)就行
```cpp=
void TCA9548A(uint8_t bus) {
Wire.beginTransmission(0x70); // TCA9548A address is 0x70
Wire.write(1 << bus); // send byte to select bus
Wire.endTransmission();
}
```
#### ADC
##### 用途
ADC( Analog Digital Convertor )是一種可以將類比訊號轉換成數位形式的芯片,透過量測輸入電壓與參照電壓之間的比例,在比較之後將轉換的數值傳給控制器
##### 使用原因
Arduino雖然自帶讀取類比輸入的腳位,但要讀取96組數據還遠遠不夠,因此需要外接可以讀取多筆輸入的ADC
##### 接線

基本上就是看到甚麼就接什麼東西,比較需要注意的是ADDR腳位的連接,ADDR是用來設定通訊位址的。設定方式如下:

##### 使用方法
Adafruit有已經寫好的函式庫,所以不需要特別去處理通訊資料傳輸接收。
基本上會用到幾個函式:
- 宣告
宣告ADS系列的芯片物件
```cpp=
ADS1115 ADS(0x48); // 宣告一個 ADS1115的芯片,並且設定他的通訊位址
ADS.begin(); // 啟用 ADS芯片
ADS.setGain(0); // 設定參照電壓 6.144 volt
ADS.setDataRate(7); // 設定通訊速率 fast
ADS.setMode(0); // 設定讀取模式 continuous mode
```
- 讀值
```cpp=
float f = ADS.toVoltage(1); //宣告數值電壓轉換參數
int16_t _val[ 4 ] = { 0 }; //宣告各腳位讀值
float Voltage[ 4 ] = { 0 }; //宣告各腳位電壓
for( int i = 0; i < 4; i++ ){
_val[i] = ADS.readADC( i ); //讀取各個腳位的值
Voltage[ i ] = _val[ i ] * f; //將讀值轉換成電壓
}
```
#### NTC
##### 用途
NTC是熱敏電阻的一種分類,國高中物理有教,電阻的電阻值會隨著環境溫度的上升而下降,但一般電阻的下降值並不明顯,熱敏電阻就是一種電阻值會隨著環境溫度改變產生明顯變化的電阻。
一般熱敏電阻有兩種,NTC&PTC
NTC電阻的電阻值會隨著溫度的上升而下降,PTC的電阻值則會歲著溫度的上升而上升
FASE官方的規定寫說硬體電路的元件不可以使用PTC元件,這點要注意。
##### 使用原因
需要測量電芯溫度
##### 計算方式
熱敏電阻有一個很重要的參數: $\beta$
$\beta_{T_1/T_2}=\frac{ln( \frac{R_1}{R_2} )}{ (\frac{1}{T_1}-\frac{1}{T_2})}$
$\beta_{T_1/T_2}$代表著NTC電組值在溫度$T_1$與$T_2$的差異,也就是說,只要有在某個溫度的電阻值以及他的$\beta$值,就可以推出NTC在另一個溫度的電阻值,有了兩點的數值之後,就可以得到電組-溫度的曲線圖,就可以推出電阻值在不同溫度的近似值,誤差範圍在0.1~0.3℃之間。
##### 計算過程
###### NTC電組溫度換算
點進[DataSheet](https://www.mouser.tw/datasheet/2/281/1/NXFT15XH103FA2B100-2936858.pdf),可以看到官方有給在25℃時的電阻值與四組$\beta$,也就是說,我們可以得出這個NTC在五個溫度時的電阻值,已知NTC的電阻與溫度關係大致與$R=ne^{kT}$相符,因此我們可以由五個已知點做近似得出這家NTC的$R(T)$。計算過程與公式圖表都在以下檔案中:
<iframe src="https://docs.google.com/spreadsheets/d/e/2PACX-1vT1_jw1NvYORTYyy9T1VZfOTvfp1JZaqIfKHyfn_5nw46MufquIWuzLDA1k_EtVouV-Bkf1yPb0VTwH/pubhtml?gid=1112537462&single=true&widget=true&headers=false" width = "105%" height = 450></iframe>
[待補]()
###### NTC電組測量

上圖為一個分壓電路,根據分壓電路計算,可以得出NTC的電阻值$R=R_X\bullet( \frac{V_{in}-V_{out}}{V_{in}} )$
其中$V_{in}$為輸入電壓,$V_{out}$為分壓電路分出來的電壓。
##### 程式
```cpp=
//雖說Arduino語法語C++類似,但常用函式庫並沒有通用
#include<math.h>
//< functions >
//Natural log 自然對數計算 Arduino沒有內建的ln()
double ln( float N ) {
return double( log(N) / log(M_E) );
}
//R to Kelvin Temp 電阻值轉絕對溫度
double R2K( double R ) {
return -( ln( R / pow(10, 8) ) ) / 0.031;
}
//R to C 電阻值轉攝氏溫標
double R2C( double R ) {
return -( ln( R / 20796 ) ) / 0.031;
}
//V to R 依照不同測試條件輸入不同參數
float V2R( float v ) {
return 10000 * float ( (5 - v) / v );
}
float V2R( float v, float Vref ) {
return 10000 * float ( (Vref - v) / v );
}
float V2R( float v, float Vref, float Rx ) {
return Rx * float ( (Vref - v) / v );
}
```
## 1.0版測試結果
> 測試方式: 電池接負載量測電池升溫狀況
> 數據格式: csv
> 記錄方式: 將Serial輸出格式調整為csv,結束測量的同時將數據複製儲存,以利後續處理。
> 輸出數據:
[MUX_ADC.csv](https://docs.google.com/spreadsheets/d/1NBPP9Wy_1shrK7nx-FktpjB3s1GwmS8JT5EraRn-_Mc/edit?usp=sharing)

## 1.0版改進方向
> 為了方便維護以及簡化主程式,之後要把程式寫成一個個物件以及簡化需要的參數。此外,之後可能會使用不同型號的ADC,所以需要因應不同芯片對程式碼做調整,挑選芯片時就會查找有沒有對應的函式庫。
:::warning
填寫完請將此表的超連結(github上的)放至[這裡](https://docs.google.com/spreadsheets/d/1slk_8V2VJco42WuU0_OY8UjR3JOtOSg_u7ZOxh3JsI0/edit#gid=0)
:::