---
# System prepended metadata

title: 'Project : FuTai VF-Model'
tags: [NHY]

---

---
tags: NHY
---
# Project : FuTai VF-Model

## Description
:::info
In this document, it will only show the soft ware logic for doing this project (cause I dont know how to do hardware :wink:)
:::

This is a model controling a VR(Variable Resistor) to change the AD that MCU get then mapping to the PWM register to control Duty cycle of PWM.

Is like :
<!-- it should be graph here-->
VR -> MCU -> PWM Duty

there is the things we use as following below:
> MCU :robot_face: : PIC16F1936
> VR : 5K Ω
> FOSC : 4Mshz
> PWM freq: 100hz

## Logic
Then to finish the program works, complet the following step below.

### 1. AD capture
To capture AD value, we use polling on timer interrupt to do the work. ~~(even the better way is use AD interrupt)~~

Two parts of function : 
a. TIMER interrupt scanning (2ms)
b. evaluate and get average value

#### a. TIMER interrupt scanning (~~2~~6ms)

Description: 
1. select channel (it works when there are many channel)
2. Activate ad scanning
3. After scanning finished, get the value of ADRES(can get full ad val)
4. back to 1.

```c=
// timer SFR
void AD_SCAN_CALLBACK(void) {

    if (AD_SAMPLE_STATE == 0) {
        // select channel
        ADCON0 = DEF_AD_CH_03;
    } else if (AD_SAMPLE_STATE == 1) {
        //Go
        ADCON0bits.GO = true;
    } else if (AD_SAMPLE_STATE == 2) {
        //get AD
        Adlist.AdValue = ADRES;
        Adlist.isDone = true;
    }
    AD_SAMPLE_STATE++;
    if (AD_SAMPLE_STATE > 3) {
        AD_SAMPLE_STATE = 0;
    }
}
```

#### b. evaluate and get average value
1. when ad scanning done => add value to sample count
2. when have AD_COUNT_MAX's Data => get average value of data

```c=
// in Main
void ADC_CTRL(void) {

    if (Adlist.isDone) {
        Adlist.isDone = false; // flag bounce back

        Adlist.SampleCount ++; // add sample count
        Adlist.AvgValue = Adlist.AvgValue + Adlist.AdValue; // avg + ad

        //and for every 10 times, sum it
        if (Adlist.SampleCount >= AD_COUNT_MAX) {
            Adlist.SampleCount = 0;
            Adlist.AvgValue = Adlist.AvgValue / AD_COUNT_MAX;
            AD1 = Adlist.AdValue;
            Adlist.AvgValue = 0;
        }
    }
}
```
### 2. Set TMR(n)~n=2~
Timer 2 is related to pwm output frquency.

![](https://i.imgur.com/jLg14d6.png)
* Here is some information about timer 2 / 4 / 6
Acroding to the formula 

$$
PWM Period = [(PRx + 1)]* 4 * TOSC *(TMRx Prescale Value)
$$
 **Note 1: TOSC = 1/FOSC**

Then
$$
[(PRx + 1)] = PWM Period* \frac{1}{TOSC} * \frac{1}{4} * \frac {1}{(TMRx Prescale Value)}
$$

So here is our setting : 
FOSC : 4mshz
TMR2 Prescale val : 64
PWM Period : 1 / 100 ~(100hz)~

Then : 
$$
PR2 + 1 = 156.25 \\
PR2 + 1 \approx156 \\
PR2 \approx 155
$$

TIMER INFO :
> FOSC = 4mhz
> prescale level : 64
> PR2 = 155
> TIMER frequency ≈ 100
> 

### 3. PWM setting

#### Duty cycle

$$
Duty\,Cycle\,Ratial = \frac{CCPRxL:CCPxCON<5:4>(DCxB)}{4*(PRx +1)}  
$$

Then: 



| Duty Cycle % | CCPRxL:DCXB |
| ------------ | ----------- |
| 100          | 624         |
|  96            | 599            |
|  20            | 125            |

Acroding to the table, we can get expect value between Duty Cycle and CCPRxL:DCXB. It will list in Table below.



## Table used

Exception formula : 

* Votage -> AD :
$$
AD~ex~ =  ( V~in~ - V~min~) - \frac {(AD~max~-AD~min~)}{(V~max~ - V~min~)} + AD~min~
$$

AD -> Duty:
$$
 Duty = ( AD~in~ - AD~min~) - \frac {(Duty~max~-Duty~min~)}{(AD~max~ - AD~min~)} + Duty~min~
$$

| V    | AD~exp~ | AD~in~ | PWM~ct~ | label :label: |
| ---- | ------- | ------ | -------- | ------------- |
| 0.25 | 50      | 50     |   125       | AD~min~  Duty~min~     |
| 0.5  | 100.3   | 81     |    151      |               |
| 0.75 | 150.6   | 128    |      177    |               |
| 1    | 201     | 186    |     204     |               |
| 1.25 | 251.3   | 239    |     230     |               |
| 1.5  | 301.6   | 295    |   257       |               |
| 1.75 | 352     | 343    |    283      |               |
| 2.0  | 402.3   | 392    |    309      |               |
| 2.25 | 452.6   | 443    |    336      |               |
| 2.5  | 503     | 497    |    362      |               |
| 2.75 | 553     | 547    |     388     |               |
| 3.0  | 603     | 599    |     415     |               |
| 3.25 | 654     | 655    |    441      |               |
| 3.5  | 704.3   | 706    |    467      |               |
| 3.75 | 754.6   | 755    |    494      |               |
| 4    | 805     | 805    |    520      |               |
| 4.25 | 855.3   | 854    |    546      |               |
| 4.5  | 905.6   | 908    |     573     |               |
| 4.75 | 956     | 965    |     599     | AD~max~  Duty~max~     |



## Problem noted

### 1. Case driven
Coding system with case driven can easily control with each state

**Using :**
```C=
enum _DEF_SYS_STATE {
    DEF_POW_ON,
    DEF_POW_OFF,
}

unsigned char STATE_CTRL;

void sys() {
    switch(STATE_CTRL) {
        case DEF_POW_ON:
            // do something
            break;
        case DEF_POW_ON:
            // do something
        break;
    }
}
```

### 2. Re Opening 
when pwm on, re turn on the pwm on register will let PWM wave not stable, it will blink like a flash at the raising edge of the wave.

**Using: a flag to control entrance to register**

```C=
void PWM_ON() {
    if(is_PWMCONV) {
        return;
    }
    // PWM ON
 }
void PWM_OFF() {
    if(!is_PWMCONV) {
        return;
    }
    //PWM OFF
}
```

### 3. AD list scan out of bound index
When scanning with array, the index out of bound to make resault of caculation changed somtimes. Make sure the index access are all correct to control your array.


## Contect

Auther : YUHUA,HU
HackMD : @huyuhua 
email  : hu881121@gmail.com
 