PWMとは、Pulse Width Modulation の略で、直訳するとパルス幅変調となります。これは、周期
におけるONとOFFの比率を変えて制御する方法で、モータ制御や調光 (光の明るさを可変する) など
に使用されます。たとえば、LEDでは、電流をパルス状にして点灯時間を100%から50%にした場合、
見かけ上電流値を半分にしたのと同じように見えます。 このPWMで湯沸しポットの電源を操作し、お湯の温度を変えてみます。 @操作の方法は「回路図2 (CPU部分)」よりのPWM出力を「回路図1 (SSR部分)」のPWM入力につなぎ、 「回路図1 (SSR部分)」のACソケットよりポットの電源を供給すると、ポットはPWMの出力で操作されます。 PWMの1周期は210ミリ秒で、オンで始まり、指定された位置 (%) でオフします。 A温度監視はIC化温度センサーLM35DZ (「写真」のポットの側面、「回路図2」) で取った温度データを RS-232C (115200 8-N-1) で出力します。 測定温度範囲は 0.0〜110.0℃です。 温度測定、PWMのオンオフ時間の設定 (%) 、RS-232Cの出力は1秒毎です。 実験してみます。 目標値 (50℃) >= 測定値 ならPWM オン時間100% 目標値 (50℃) < 測定値 ならPWM オン時間0% 上記の条件はオンオフ動作です。 実験時の「RS-232C (115200 8-N-1) での転送データ画面」、 実験結果の「RS-232C (115200 8-N-1) での転送データより作成されたグラフ」、 実験に使用した「ソフト」を下に記載しておきます。 |
写真
ナショナル電気ポット NC-525
1.2リットル 500W
回路図1 (SSR部分)
回路図2 (CPU部分)
RS-232C (115200 8-N-1) での転送データ画面
50.5, mv = -50 ..... 50.5℃ 電源スイッチをオフ
50.4, mv = -50 ..... 50.4℃ 電源スイッチをオフ
50.3, mv = -50 ..... 50.3℃ 電源スイッチをオフ
50.2, mv = -50 ..... 50.2℃ 電源スイッチをオフ
50.1, mv = -50 ..... 50.1℃ 電源スイッチをオフ
50.0, mv = 50 ..... 50.0℃ 電源スイッチをオン
49.9, mv = 50 ..... 49.9℃ 電源スイッチをオン
49.8, mv = 50 ..... 49.8℃ 電源スイッチをオン
49.7, mv = 50 ..... 49.7℃ 電源スイッチをオン
49.6, mv = 50 ..... 49.6℃ 電源スイッチをオン
49.5, mv = 50 ..... 49.5℃ 電源スイッチをオン
RS-232C (115200 8-N-1) での転送データより作成されたグラフ
How to Compile (コンパイルのしかた) プログラム /* * temp_ctrl.c * * Created: 2017/03/14 19:53:56 * Author : User */ /* * オンオフ動作 * * ATmega328P * * * Fuses(システムクロック:20MHz) * * * ディホルト Int. RC Osc. 水晶 外部クロック * DISABLED 2.7V 2.7V 2.7V * 8/8MHz 8MHz * EXTENDED 0xFF 0xFD 0xFD 0xFD * HIGH 0xD9 0xD9 0xD9 0xD9 * LOW 0x62 0xE2 0xF7 0xE0 */ #define _SV_ 50.0 /* 目標値 (℃)*/ #define _SPAN_ 100 /* 測定スパン (測定範囲の最大値と最小値の差) */ #include <stdio.h> /* sprintf() */ #include <string.h> /* strlen() */ #include <avr/io.h> #include <avr/interrupt.h> ///#define F_CPU 20.0E6 /* 20 MHz */ ///#include <util/delay.h> #define USEC 1024 #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) typedef struct t_systim { short utime; /* 上位16bit */ unsigned long ltime; /* 下位32bit */ } SYSTIM; ///void delay_us(unsigned short t){while(t--) _delay_us(1);} ///void delay_ms(unsigned short t){while(t--) _delay_ms(1);} short ON_OFF(float SV, float PV); void initADC(void); void initTIMER(void); void get_tim(SYSTIM *p_tim); void initUSART0(void); void Send0(char *str); short ad_data; SYSTIM SYSCK; int main(void) { SYSTIM tim; static unsigned long ot[2]; float ondo; /* 温度 (0.0 〜 110.0℃) */ short mv; /* 偏差による操作量 (%) */ short pwm_on_time; /* PWMオン時間 (0、100%) */ long tmp; char buff[32], buff2[8]; int len; /* ATmega328P */ DDRB = 0b00000101; /* XTAL2 XTAL1 SCK MISO MOSI OC1B ----- LED */ DDRC = 0b00000000; /* xxxxx RESET ----- ----- ----- ----- ----- ----- */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111010; PORTC = 0b00111110; PORTD = 0b11111100; initADC(); initTIMER(); initUSART0(); /* * WGM13 WGM12 WGM11 WGM10 動作種別 TOP値 * 1 1 1 1 高速PWM動作 OCR1A * * COM1A1 COM1A0 COM1B0 COM1B0 * 0 0 1 0 比較一致でLow、BOTTOMでHighをOC1Bピンへ出力(非反転動作) */ TCCR1A = 0b00100011; /* 一致時(OCR1B)Low, TOP->BOTTOM(OCR1A)でHighを */ /* OC1Bピンへ出力(非反転動作), 高速PWM動作 */ /// TCCR1B = 0b00011001; /* (1 / 20MHz) * 65535 = 3.3mS */ /// TCCR1B = 0b00011010; /* (8 / 20MHz) * 65535 = 26mS */ TCCR1B = 0b00011011; /* (64 / 20MHz) * 65535 = 210mS */ /// TCCR1B = 0b00011100; /* (256 / 20MHz) * 65535 = 839mS */ /// TCCR1B = 0b00011101; /* (1024 / 20MHz) * 65535 = 3355mS */ OCR1A = 0xffff; /* TOP->BOTTOMでHigh, 固定 */ OCR1B = 0; /* 一致時Low、可変(0はPWM出力on時間0%) */ sei(); Send0("START\r"); get_tim(&tim); ot[0] = tim.ltime; ot[1] = tim.ltime; while(1){ get_tim(&tim); if((unsigned long)(tim.ltime - ot[0]) >= 500000/USEC){ /* 500mS */ ot[0] = tim.ltime; PORTB ^= 0b00000001; } get_tim(&tim); if((unsigned long)(tim.ltime - ot[1]) >= 1000000/USEC){ /* 1S */ ot[1] = tim.ltime; /* 情報伝達部 */ /* 温度を得る 例 12.3℃ */ ondo = 1100.0 * ad_data / 10230; /* 0.0〜110.0℃ */ /* 情報伝達部 */ /* 偏差による操作量を得る (-50, 50) */ mv = ON_OFF(_SV_, ondo); /* 電力制御部 */ if(mv >= 0) /* mv = 0 〜 50% */ pwm_on_time = mv * 2; /* pwm_on_time = 0 〜 100% */ else pwm_on_time = 0; /* pwm_on_time = 0% */ tmp = round(65535.0 * pwm_on_time / 100.0); if(tmp > 65535) tmp = 65535; OCR1B = (short)tmp; /* 表示例 49.9, mv = 50 */ sprintf(buff2, "%ld", round(ondo * 10)); len = strlen(buff2); buff2[len + 1] = buff2[len]; buff2[len] = buff2[len - 1]; buff2[len - 1] = '.'; sprintf(buff, "%s, mv = %d\r", buff2, mv); Send0(buff); } } } /* SV 目標値 * PV 測定値 * 戻り値 偏差による操作量 (-50, 50) */ short ON_OFF(float SV, float PV) { float e; /* 偏差 (%) */ short mv; /* 偏差による操作量 (%) */ /* 偏差(%)を取得 */ e = 100.0 * (SV - PV) / _SPAN_; if(e >= 0.0) mv = 50; else mv = -50; return mv; } void initADC(void) { ADCSRA = 0b10101111; /* AD変換イネーブル, AD自動変換イネーブル,AD変換割り込みイネーブル */ /* 1/128クロック(最大変換時間=200uS @16MHz, 156uS @20MHz) */ ADCSRB = 0b00000011; /* タイマ/カウンタ0比較一致A */ ADMUX = 0b11000000; /* 基準電圧 = 1.1V(@atmega328p), 右詰, アナログ入力 = ADC0 */ } ISR(ADC_vect) { static long result; static short counter; result += ADC; counter++; if(counter >= 100){ counter = 0; ad_data = (short)(result / 100); result = 0; } } void initTIMER(void) { TCCR0A = 0b00000010; /* CTC動作 */ TCCR0B = 0b00000100; /* clk / 256 */ OCR0A = 80 - 1; /* (256 / 20MHz) * 80 = 1024uS */ TIMSK0 = 0b00000010; /* 比較A割り込み要求有効 */ } ISR(TIMER0_COMPA_vect) { if(++SYSCK.ltime == 0) ++SYSCK.utime; } void get_tim(SYSTIM *p_tim) { cli(); *p_tim = SYSCK; sei(); } void initUSART0(void) { UBRR0 = 21; /* U2X0 = 1, 115200ボー/20MHzクロック */ UCSR0A = 0b00000010; /* U2X0 = 1 */ UCSR0B = 0b00001000; /* 送信イネーブル */ UCSR0C = 0b00000110; /* 8ビット, パリティなし, ストップ・ビット1 */ } /* * 1データ送信 */ void Tx0(char data) { while((UCSR0A & _BV(5)) == 0) ; UDR0 = (unsigned char)data; } /* * 文字列を送信 */ void Send0(char *str) { while(*str != '\0'){ Tx0(*str); str++; } } |