概述
一般儀器儀表的信號電流都為4-20mA,指最小電流為4mA,最大電流為20mA。傳輸信號時候,要考慮到導線上也有電阻,如果用電壓傳輸則會在導線的產生一定的壓降,那接收端的信號就會產生一定的誤差,所以使用電流信號作為變送器的標準傳輸。為什么選擇4-20mA而不是0-20mA呢?4ma而不是0ma是用來檢測線路開路的,如果0是最小,那么開路故障就檢測不到了。為了解決上述問題和避開相關噪聲的影響,我們用電流來傳輸信號,因為電流對噪聲并不敏感。4~20mA的電流環便是用4mA表示零信號,用20mA表示信號的滿刻度,而低于4mA高于20mA的信號用于各種故障的報警。
很多控制器接受來自各種檢測儀表的0~20mA或4~20mA電流,制作一個0-20mA的的信號發生器,可以給很多儀器儀表做校準或測試。
2. 總體方案設計
2.1. 設計目標
單片機控制4路PWM,輸出的PWM信號控制輸出電壓在0-3.0V之間,經過電流電壓轉電流電路變成電流信號,輸出電流在0-20mA之間。按鍵調節輸出電流,oled實時顯示4路電流值。
2.2. 設計思路
芯片選擇STM32F030C8T6,帶4路PWM輸出,性價比高,同時支持rt-thread操作系統,這里我們選用2.1的版本。OLED選用中景園電子0-96寸OLED。
2.2.1.硬件電路原理
圖1是一個電壓轉電流的典型電路。單片機輸出PWM,控制Vi電壓電平在0-3V之間,RL流過的電流為0-20ma。
圖1 電壓轉電流電路
圖2 OLED顯示電路
由于MCU內部可配置上拉電阻,所以可以直接將按鍵接到MCU上。
圖3 按鍵電路
2.2.2.軟件設計流圖
2.2.3.關鍵代碼
按鍵部分代碼。
/* key thread entry */
staticvoid 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_state1[i] = rt_hw_key(i); } rt_thread_delay(RT_TICK_PER_SECOND / 20); for (i=KEY1; i key_state2[i] = rt_hw_key(i); } for (i=KEY1; 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 %dclicked ', 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 ', 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顯示部分代碼。 /* oled thread entry */ staticvoid oled_thread_entry(void*parameter) { uint8_t i; rt_base_tlevel; 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.RTT使用情況 按鍵的初始化函數可以不用再main函數中添加,而是在key.c中調用下面的函數就可以了INITDEVICEEXPORT(rthwkey_init),代碼看起來干凈整潔。通過scons來裁剪配置系統,不需要人為刪減代碼,非常好用。Finsh組件,方便調試,方便自定義添加串口命令。代碼類linux編程風格。提供POSIX 標準接口,上層代碼移植方便。
上一篇:RCC時鐘控制系統組成及時鐘原程序分析
下一篇:一文知道串口通信的幀同步問題
推薦閱讀最新更新時間:2025-06-07 23:35


