# 軟體 PWM 和電路 ## 學習目標 * 延續 [Raspberry Pi GPIO](https://hackmd.io/s/SkydMvN0b),透過電表來測量電壓 * video: [Lab Example](https://www.youtube.com/watch?v=KbAsGLYd6UY) * 透過 PWM 改變輸出電壓 * video: [壓量測教學](https://www.youtube.com/watch?v=w8QRUl65sw8) * 學習 [high-resolution timer](https://elinux.org/High_Resolution_Timers) 與對應函式的使用 ## PID 控制器原理 PID控制器可以用來控制任何可被測量及可被控制變數。比如,它可以用來控制溫度、壓力、流量、化學成分、速度等等。汽車上的巡航定速功能就是一個例子。 下圖說明只要改變「比例、積分和微分」,就可以對被控對象進行想要的控制: ![](https://i.imgur.com/dujqpBj.png) * 延伸閱讀: * [一個小故事,了解 PID 控制原理](https://www.guokr.com/post/699125/) * [PID 溫度控制器開發](http://blog.ncue.edu.tw/sys/lib/read_attach.php?id=17440) ## Linux Real-time extension * Real Time (RT) 的定義 * 一個 RT 的系統需要保證其工作在給定的時間限制內完成 (稱為deadline)。 系統不需要以最快的速度 (real fast) 完成任務,但需要時常或每次皆在 deadline 之內完成。 * 在這個前提下,RT 系統的任務完成時間是可確定的 (deterministic)。 而根據系統的限制不同,RT 可分為: * Soft RT - 系統不一定每次皆需要遵守 deadline,但較多的 deadline miss 會導致服務品質降低。 * Hard RT - 系統能每次皆能在 deadline 內完成任務。 - [ ] 展示影片 * [No Xenomai, high system load](https://www.youtube.com/watch?v=Ok1rAPYTmQc) * [With Xenomai and high system load](https://www.youtube.com/watch?v=SJizUTmB_jc) - [ ] 延伸閱讀: [Xenomai](http://wiki.csie.ncku.edu.tw/embedded/xenomai) ## HRT - [ ] `wave.c` ```C #include <stdlib.h> #include <stdio.h> #include <time.h> #include <sched.h> #define NSEC_PER_SEC 1000000000 /* the struct timespec consists of nanoseconds * and seconds. if the nanoseconds are getting * bigger than 1000000000 (= 1 second) the * variable containing seconds has to be * incremented and the nanoseconds decremented * by 1000000000. */ static inline void tsnorm(struct timespec *ts) { while (ts->tv_nsec >= NSEC_PER_SEC) { ts->tv_nsec -= NSEC_PER_SEC; ts->tv_sec++; } } int main(int argc, char **argv) { struct timespec t; struct sched_param param; /* default interval = 50000ns = 50us * cycle duration = 100us */ int interval = 50000; if (argc >= 2 && atoi(argv[1]) > 0) { printf("using realtime, priority: %d\n",atoi(argv[1])); param.sched_priority = atoi(argv[1]); /* enable realtime fifo scheduling */ if(sched_setscheduler(0, SCHED_FIFO, &param)==-1){ perror("sched_setscheduler failed"); exit(-1); } } if (argc >= 3) interval = atoi(argv[2]); /* get current time */ clock_gettime(0, &t); /* start after one second */ t.tv_sec++; while (1) { /* wait untill next shot */ clock_nanosleep(0, TIMER_ABSTIME, &t, NULL); /* do the stuff */ GPIO_OPS(); /* Change this line */ /* calculate next shot */ t.tv_nsec += interval; tsnorm(&t); } return 0; } ``` * 編譯和執行 ```shell $ gcc -o wave wave.c -lrt $ ./wave ``` - [ ] [clock_nanosleep](http://man7.org/linux/man-pages/man2/clock_nanosleep.2.html) ## 實作:呼吸燈 * 參考 [Arduino練習:呼吸燈](https://yehnan.blogspot.tw/2012/02/arduino_16.html) 和 [树莓派GPIO入门02-GPIO控制LED亮度,制作呼吸灯效果](http://blog.mangolovecarrot.net/2015/04/28/raspi-study02/) * video: [製作會呼吸的LED燈](https://www.youtube.com/watch?v=2OTua6U7CY8) * 用上述 [clock_nanosleep](http://man7.org/linux/man-pages/man2/clock_nanosleep.2.html) 精準控制時間,製作出呼吸燈效果 ## code - [name=Young Sting] ```clike #define PI 3.14159265 ...... int all = 2000; int x = 0; /* Hold the status (By pulling up and down repeatedly for 300 times) * to make it easier for us to observe the effect */ double val = PI / 180.0; int count = 270; int cnt = 1; while( 1 ) { /* Write GPIO value */ double tmp = all * ( sin(val*count) + 1 ); GPIOWrite(PIN, 0); usleep( all*2 - tmp ); GPIOWrite(PIN, 1); usleep( tmp ); //printf("%d %d\n",x ,all-x); if( (cnt++) == 10 ){ count += 3; if( count == 360 ) count = 0; cnt = 0; } } return 0; ```