DEV Community

Cover image for Design a 0-20mA Electric Current Generator.
Abby
Abby

Posted on

Design a 0-20mA Electric Current Generator.

Alt Text

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

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

Alt Text

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

Figure 3 Key circuit

2.2.2.Software Design

Alt Text

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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 | Github | Twitter | Facebook | Youtube

Top comments (1)

Collapse
 
akaaykohli profile image
akaaykohli

Your project design using the STM32F030C8T6, 0.96-inch OLED, and RT-Thread OS for a 4-20mA signal generator is impressive. The hardware and software integration, including the voltage-to-current circuit and OLED display, demonstrates a well-thought-out approach.

Your use of four PWM channels to control the output voltage and subsequently the current is a clever design choice. The key thread and OLED display thread implementation provide real-time adjustment and monitoring, enhancing user interaction.

Your positive feedback on using RT-Thread OS, particularly highlighting its clean code structure, system configuration with SCons, and ease of debugging with Finsh components, is valuable for those considering similar projects.

Overall, your detailed electriciandiary explanation and code snippets provide insights into a robust system for generating and controlling current signals. Well done!