---
# System prepended metadata

title: MCP3421_電壓偵測IC
tags: [PIC16F18854, datasheet, mcp3421]

---

# MCP3421_電壓偵測IC
[MCB3421 datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/22003e.pdf)
###### tags:`datasheet` `mcp3421` `PIC16F18854`
<style> .red {color:red;} </style> 
## IC封裝與腳位
![](https://i.imgur.com/sTZe9A9.png)

## 寫入控制暫存器
![](https://i.imgur.com/FRlgyRb.png)
### <span class ="red">$\bar {RAY}$</span>:準備位
用**讀取**命令讀取RDY位：\
1 =輸出寄存器尚未更新。\
0 =輸出寄存器已更新為最新的轉換結果。\
\
用**寫**命令寫RDY位：\
連續轉換模式：無影響\
\
單次轉換模式：\
1 =啟動新轉換。\
0 =無效果。

### <span class ="red">$\bar O/C$</span>:轉換模式位
1 =連續轉換模式（默認）。 該設備連續執行數據轉換。\
0 =單次轉換模式。器件執行單次轉換並進入低功耗待機模式，直到收到另一個寫或讀命令為止。

### <span class ="red">${S1-S0(3:2)}$</span>:採樣率選擇位
00 = 240 SPS (12 bits) (預設)\
01 = 60 SPS (14 bits)\
10 = 15 SPS (16 bits\
11 = 3.75 SPS (18 bits)

### <span class ="red">${G1-G0(1:0)}$</span>:PGA增益選擇位
00 = x1（預設）
01 = x2 
10 = x4 
11 = x8

## 使用方式
::: info
### 接線方式
![](https://i.imgur.com/MQbbvG7.png)
$量測的電壓={OutPut\ Code}_{(10)}\times下下表對應的LSB\times{{R_1+R_2}\over R_2}\times {1\over 所設定的PGA}$
:::

::: warning
### OutPut Code
![](https://i.imgur.com/dFY2jHJ.png)
可以用這個驗證output code正確與否\
\
其中的2.048v就代表${V_{in}}^++-{V_{in}}^-$不可以超過2.048v，不然再成Maximum Code與PGA的值會超過Maximum Code能表示的範圍，所以如果要量測比2.048V大就要將電壓經過分壓後再讀取進來，如前面的接線方式圖

### 每階可以辨識的電壓
![](https://i.imgur.com/uXnUPgo.png)
可以想像成將2.048v 區分成很多等份，而每一等份的實際電壓為LSB
例如: 用12bits的可以1mV的精度分辨

### 公式可以用到的對照表
![](https://i.imgur.com/BAEWUx7.png)
MSB(最高位)是用來表是量測電壓的正負，0為正、1為負
\
例如: 用12bits的最高值是 ${(0111\ 1111\ 1111)}_{(2)}\ = \ 2047_{(10)}$

### 如何閱讀輸出的二進制
![](https://i.imgur.com/oMlMqpc.png)
為了1個byte1個byte對應著SCL的顯示，所以如果是無法用1byte(8bits)整除的轉換選項就會在最高位元側顯示這筆data的MSB，也就是標示正負號的位元
\
例如: 
12bits模式的$D11-D0=(1111\ 00000\ 0000)_{(2)}$，其MSB為'1'，所以會顯示成$(1111\ 1111\ 00000\ 0000)_{(2)}$

要捨棄掉最前面重複的MSB才是用來計算的Output Code

### 計算量測電壓實際值
![](https://i.imgur.com/RXUNkjR.png)
例如:
$12bits\ 模式\\Outtput\ Code={(1010\ 1010\ 1010)}_{(2)}=(-682)_{(10)}對應的LSB為1mV\\R_1=R_2=1kΩ\\PGA=1$
\
則:
量測電壓為$=-682 \times 1m \times {{1k+1k}\over 1k} \times {1\over1}=-1.364V$
:::

::: success
### 要來寫程式了喔
#### WRITE
![](https://i.imgur.com/cvLoFSf.png)
$START \to 7位地址+寫位(0) \to 寫入控制暫存器8位 \to STOP$

**地址位為1101000**
#### READ
![](https://i.imgur.com/Jtui7Hm.png)

$START \to 7位地址+讀位(1) \to 讀到的資料 \to 寫入控制暫存器8位 \to STOP$
如果要停止讀出資料就傳送STOP或是NACK
在"寫入控制暫存器8位"之後如果還繼續有SCL產生(例如還沒STOP)就會一直重複著"寫入控制暫存器8位"

本篇所有的"$\to$"都是ACK
### 程式說明
::: spoiler
### 大家都要有的初始化
```c=
void init(void){
    PMD_Initialize();
    PIN_MANAGER_Initialize();
    OSCILLATOR_Initialize();
}

void OSCILLATOR_Initialize(void){
    // NOSC HFINTOSC; NDIV 1; 
    OSCCON1 = 0x60;
    // CSWHOLD may proceed; SOSCPWR Low power; 
    OSCCON3 = 0x00;
    // MFOEN disabled; LFOEN disabled; ADOEN disabled; SOSCEN disabled; EXTOEN disabled; HFOEN disabled; 
    OSCEN = 0x00;
    // HFFRQ 4_MHz; 
    OSCFRQ = 0x02;
    // HFTUN 0; 
    OSCTUNE = 0x00;
}

void PMD_Initialize(void){
    // CLKRMD CLKR enabled; SYSCMD SYSCLK enabled; SCANMD SCANNER enabled; FVRMD FVR enabled; IOCMD IOC enabled; CRCMD CRC enabled; NVMMD NVM enabled; 
    PMD0 = 0x00;
    // TMR0MD TMR0 enabled; TMR1MD TMR1 enabled; TMR4MD TMR4 enabled; TMR5MD TMR5 enabled; TMR2MD TMR2 enabled; TMR3MD TMR3 enabled; NCOMD DDS(NCO) enabled; TMR6MD TMR6 enabled; 
    PMD1 = 0x00;
    // ZCDMD ZCD enabled; DACMD DAC enabled; CMP1MD CMP1 enabled; ADCMD ADC enabled; CMP2MD CMP2 enabled; 
    PMD2 = 0x00;
    // CCP2MD CCP2 enabled; CCP1MD CCP1 enabled; CCP4MD CCP4 enabled; CCP3MD CCP3 enabled; CCP5MD CCP5 enabled; PWM6MD PWM6 enabled; PWM7MD PWM7 enabled; 
    PMD3 = 0x00;
    // CWG3MD CWG3 enabled; CWG2MD CWG2 enabled; CWG1MD CWG1 enabled; MSSP1MD MSSP1 enabled; UART1MD EUSART enabled; MSSP2MD MSSP2 enabled; 
    PMD4 = 0x00;
    // DSMMD DSM enabled; CLC3MD CLC3 enabled; CLC4MD CLC4 enabled; SMT1MD SMT1 enabled; SMT2MD SMT2 enabled; CLC1MD CLC1 enabled; CLC2MD CLC2 enabled; 
    PMD5 = 0x00;
}

void PIN_MANAGER_Initialize(void){
    /**
    LATx registers
    */
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;

    /**
    TRISx registers
    */
    TRISA = 0x00;
    TRISB = 0xFF;
    TRISC = 0xFF;

    /**
    ANSELx registers
    */
    ANSELC = 0xE7;//RC3 RC4設為數位
    ANSELB = 0xFF;
    ANSELA = 0x00;//PORTA 設為數位

    /**
    WPUx registers
    */
    WPUE = 0x00;
    WPUB = 0x00;
    WPUA = 0x00;
    WPUC = 0x00;

    /**
    ODx registers
    */
    ODCONA = 0x00;
    ODCONB = 0x00;
    ODCONC = 0x00;

    /**
    SLRCONx registers
    */
    SLRCONA = 0xFF;
    SLRCONB = 0xFF;
    SLRCONC = 0xFF;

    /**
    INLVLx registers
    */
    INLVLA = 0x00;
    INLVLB = 0xFF;
    INLVLC = 0xFF;
    INLVLE = 0x08;

//智能SDA和SCL兩隻腳
    SSP1CLKPPS = 0x13;   //RC3->MSSP1:SCL1;    
    RC3PPS = 0x14;   //RC3->MSSP1:SCL1;    
    RC4PPS = 0x15;   //RC4->MSSP1:SDA1;    
    SSP1DATPPS = 0x14;   //RC4->MSSP1:SDA1;    
}
```


#### master暫存器設定
```c=
void master_init(void){
    INTCONbits.GIE = 0;//中斷不智能
    INTCONbits.PEIE = 0;//中斷不智能
    PIR3bits.SSP1IF = 1;//等待傳輸
    PIR3bits.BCL1IF = 1;//未檢測到衝突
    PIE3bits.SSP1IE = 0;//不允許中斷
    PIE3bits.BCL1IE = 0;//衝突不允許中斷
    
    SSP1CON1=0x28; //智能SDA和SCL兩隻腳與設定為I2C master模式
    SSP1CON2=0x00;
    SSP1CON3=0x00;
    SSP1STAT=0x00;
    SSP1BUF =0x00;
    SSP1STATbits.SMP=1; //1 =在標準速度模式（100 kHz和1 MHz）下禁用slew rate control
    SSP1STATbits.CKE=1; //1 =智能輸入邏輯，以使閾值符合SMBus規範
    SSP1ADD=0x06; //100k Hz=0x06 數字越高頻率越慢
    SSP1MSK=0xFF; //理論上這個在master應該是沒有用的，但還是讓她都為1乖一點
    RA3=1; //可以用來看這個時候轉換成master了
}
```
#### 等等喔
```c=
void I2C_Master_Wait(){
    while ((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F));//確認有發起start後再動作
}
```
#### start
```c=
void I2C_Master_Start(){
    I2C_Master_Wait();
    SSP1CON2bits.SEN = 1; //start
}
```
#### restart
```c=
void I2C_Master_RepeatedStart(){
    I2C_Master_Wait();
    SSP1CON2bits.RSEN = 1; //restart
}
```
#### stop
```c=
void I2C_Master_Stop(){
    I2C_Master_Wait();
    SSP1CON2bits.PEN = 1; //stop
}
```
#### 寫入
```c=
void I2C_Master_Write(uint8_t data){
    I2C_Master_Wait(); 
    SSP1BUF = data; //將要傳送的data放進要經由I2C傳出的buffer裡
    while(!PIR3bits.SSP1IF); // 等待bus上空閒
    PIR3bits.SSP1IF = 0; //傳輸完成之後會為1，要用軟體回歸0，代表做完事了
}
```

#### 讀取
```c=
uint8_t I2C_Master_Read(uint8_t get){//get=0代表所有讀取資料完畢後會回傳nack(通常都是這個)
                                    //get=1代表所有讀取資料完畢後會回傳nack
    uint8_t temp; // 用來儲存讀取的資料的變數
    I2C_Master_Wait(); 
    SSP1CON2bits.RCEN = 1; //智能I2C的接收模式
    I2C_Master_Wait(); 
    temp = SSP1BUF; //將buffer收到的資料放進用來存取的變數裡
    I2C_Master_Wait();
    SSP1CON2bits.ACKDT = (get)?0:1;//get=1時，ack_data=NACK(0)  //get=0時，ack_data=ACK(1)
    SSP1CON2bits.ACKEN = 1; //回傳ack_data給發送端
    return temp; //引用I2C_Master_Read(uint8_t get)這行函式可得到讀取的資料
}
```
:::
#### main
```c=
#define VOLTADE_ADD 0x68
uint8_t slave_feedback1, slave_feedback2;
void main(void) {
    I2C_Master_Start(); 
    I2C_Master_Write((VOLTADE_ADD << 1)|0); //7 bit address + Write(0)
    I2C_Master_Write(0x90);//也可以不寫，這就是預設狀態
    I2C_Master_Stop();

    I2C_Master_Start(); 
    I2C_Master_Write((VOLTADE_ADD << 1)|1); //7 bit address + Read(1)
    slave_feedback1=I2C_Master_Read(1);
    slave_feedback2=I2C_Master_Read(0);//傳NACK代表是最後一筆資料了
    I2C_Master_Stop();
    while(1){
    ;
    }
}    
```
:::


::: danger
## 注意
**最高位元是用來表示正負號，0為正，1為負**
例如一個12bit的輸出為:
0110 1010 1011，轉成十進制為1707

應該要再加東西
:::



