Design a 0-20mA Electric Current Generator.
Published: 2021-02-03 05:33:17

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nrs9iaf2ybif4qds16q0.png)


## Introduction

Generally, the signal electric current of the general instrument is 4-20mA, which means that the minimum current is 4mA and the maximum current is 20mA. When transmitting a signal, consider that there is resistance on the wire, if the voltage transmission will produce a certain voltage drop in the wire, the signal at the receiving end will produce a certain error, so the electric current signal is used as the standard transmission of the transmitter. 


Why choosing 4-20mA instead of 0-20mA? 4ma is used to detect the line open, if 0 is the smallest, then the open circuit fault can not be detected. To solve these problems and avoid the effects of related noise, we use currents to transmit signals because currents are not sensitive to noise. The current ring of 4 to 20mA is a zero signal with 4mA, a full scale of the signal with 20mA, and a signal below 4mA and above 20mA is used for various fault alarms.


Many controllers accept 0 to 20mA or 4 to 20mA currents from various instruments and make a 0-20mA signal generator that can be calibrated or tested for many instruments.

## 2. Overall Design

### 2.1. Target

The single-chip control 4 PWM, the output PWM signal control output voltage between 0-3.0V, through the current voltage to current circuit into a current signal, the output current between 0-20mA. Press the button to adjust the output current, OLED will display 4 current values in real-time.

### 2.2. Design



Taking STM32F030C8T6 for this experiment as it has 4 PWM output and great supports on opensource RT-Thread Real-Time Operating System. And I take a 0-96 inch OLED.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9v0ob007rfy3jkeizdnt.png)

### 2.2.1.Hardware circuits

Figure 1 is a typical circuit of voltage-to-current. The single-chip outputs PWM, the control Vi voltage level is between 0-3V, and the current flowing through the RL is 0-20ma.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/oto91zxs0rqzr883xod7.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qfk9pmbb3s4ufjz0alps.jpg)

Figure 2 OLED display circuit

Because the MCU can be configured with pull-up resistors, the keys can be directly connected to the MCU.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/s83uxomz79ctgetsof47.jpg)

Figure 3 Key circuit

### 2.2.2.Software Design

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nuic7v7tfe8yxdozd7li.png)

### 2.2.3. Key Point Code

Key Code.

```
/* key thread entry */
static void key_thread_entry(void* parameter)
{
    KEY_e i;
    uint8_t key_state1[KEY_NUM];
    uint8_t key_state2[KEY_NUM];
    uint8_t key_counter[KEY_NUM];
    rt_base_t level;

    memset(key_counter, 0, sizeof(key_counter));
    while(1)
    {
        for (i=KEY1; i<KEY_NUM; i++)
        {
            key_state1[i] = rt_hw_key(i);
        }
        rt_thread_delay(RT_TICK_PER_SECOND / 20);
        for (i=KEY1; i<KEY_NUM; i++)
        {
            key_state2[i] = rt_hw_key(i);
        }
        for (i=KEY1; i<KEY_NUM; i++)
        {
            if (key_state1[i] == key_state2[i] &&
                key_state1[i] == 0)
            {
                level = rt_hw_interrupt_disable();
                if (key_counter[i] == 0)
                {
                    switch(i)
                    {
                        case KEY2:
                            if (pwm_channel < 3)
                            {
                                pwm_channel++;
                            }
                            break;
                        case KEY1:
                            if (pwm_channel > 0)
                                pwm_channel--;
                            break;
                        case KEY3:
                            if (pwm_value[pwm_channel] < 20000)
                                pwm_value[pwm_channel]++;
                            break;
                        case KEY4:
                            if (pwm_value[pwm_channel] > 0)
                                pwm_value[pwm_channel]--;
                            break;
                        case KEY5:
                            if (pwm_value[pwm_channel] < 16000)
                                pwm_value[pwm_channel] += 4000;
                            else
                                pwm_value[pwm_channel] = 20000;
                            break;
                        case KEY6:
                            if (pwm_value[pwm_channel] >= 4000)
                                pwm_value[pwm_channel] -= 4000;
                            else
                                pwm_value[pwm_channel] = 0;
                            break;
                    }
                    rt_kprintf("key %d clicked\r\n",  i);
                }
                if (key_counter[i] >= 5)
                {
                    switch(i)
                    {
                        case KEY2:
                            if (pwm_channel < 3)
                            {
                                pwm_channel++;
                            }
                            break;
                        case KEY1:
                            if (pwm_channel > 0)
                                pwm_channel--;
                            break;
                        case KEY3:
                            if (pwm_value[pwm_channel] < 20000)
                                pwm_value[pwm_channel]++;
                            break;
                        case KEY4:
                            if (pwm_value[pwm_channel] > 0)
                                pwm_value[pwm_channel]--;
                            break;
                        case KEY5:
                            if (pwm_value[pwm_channel] < 16000)
                                pwm_value[pwm_channel] += 4000;
                            else
                                pwm_value[pwm_channel] = 20000;
                            break;
                        case KEY6:
                            if (pwm_value[pwm_channel] >= 4000)
                                pwm_value[pwm_channel] -= 4000;
                            else
                                pwm_value[pwm_channel] = 0;
                            break;
                    }
                    rt_kprintf("key %d pressed\r\n",  i);
                }
                if (key_counter[i] < 5)
                {
                    key_counter[i]++;
                }
                rt_hw_interrupt_enable(level);
            }
            else
            {
                key_counter[i] = 0;
            }
        }
        rt_thread_delay(RT_TICK_PER_SECOND / 100);
    }
}
```

OLED display code.

```
/* oled thread entry */
static void oled_thread_entry(void* parameter)
{
    uint8_t i;
    rt_base_t level;
    char str_pwm[64];
    
    OLED_Init();
    OLED_Clear();
    PWM_TIM1(999, 1); //48MHZ/(999+1)/(1+1) = 24KHZ
    while(1)
    {
        //OLED_ShowString(0, 3,"1.3' OLED TEST");
        if ((memcmp(pwm_value_temp, pwm_value, sizeof(pwm_value)) != 0) ||
            (pwm_channel_temp != pwm_channel))
        {
            level = rt_hw_interrupt_disable();
            memcpy((char *)pwm_value_temp, (char *)pwm_value, sizeof(pwm_value));
            pwm_channel_temp = pwm_channel;
            rt_hw_interrupt_enable(level);
            for (i=0; i<4; i++)
            {
                if (pwm_channel == i)
                {
                    snprintf(str_pwm, 64, "* %2d.%03d ma", pwm_value[i]/1000, pwm_value[i]%1000);
                }
                else
                {
                    snprintf(str_pwm, 64, "  %2d.%03d ma", pwm_value[i]/1000, pwm_value[i]%1000);
                }
                OLED_ShowString(0, i*2, (uint8_t *)str_pwm);
            }
            TIM_SetCompare1(TIM1, 0.915 *(pwm_value_temp[3] * 999) / 20000);
            TIM_SetCompare2(TIM1, 0.915 *(pwm_value_temp[2] * 999) / 20000);
            TIM_SetCompare3(TIM1, 0.915 *(pwm_value_temp[1] * 999) / 20000);
            TIM_SetCompare4(TIM1, 0.915 *(pwm_value_temp[0] * 999) / 20000);
            rt_thread_delay(RT_TICK_PER_SECOND / 10);
        }
        else
        {
            //OLED_ShowString(63,6,"CODE:");    
            rt_thread_delay(RT_TICK_PER_SECOND / 10);
        }
    }
}
```

### 2.2.4.Some of my feeling about using RT-Thread OpenSource Operating System in this project.

- The initialization function of the key does not need to add main function, call `INITDEVICEEXPORT(rthwkey_init)` in `key.c` can help with, and the code looks clean and tidy. 
- Through scons to tailor and configure the system, do not need manually code tailor which is very user-friendly. 
- The RT-Thread Finsh components making debugging and custom adding serial commands very easy.
- The POSIX standard interface offering an easy way to port the upper-level code.


**RT-Thread Contact Info:**

 [Website](https://www.rt-thread.io/)  |  [Github](https://github.com/RT-Thread)  | [Twitter](https://twitter.com/rt_thread) |  [Facebook](https://www.facebook.com/RT-Thread-IoT-OS-110395723808463)   |   [Youtube](https://www.youtube.com/channel/UCdDHtIfSYPq4002r27ffqPw
)