# 軟體 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, ¶m)==-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;
```