前回投稿で利用したpigpiodのCPU使用率が高過ぎて気になったので自作ライブラリに切り替えたら5℃前後も温度が下がってしまった。
しかし、温度を下げるためのプログラムが逆に温度を上げてしまうなんて本末転倒もいいところかも。(-_-;)
【ファンが回ってるところ】
ファンは吸気方向で取り付けるようになっていたがそれだと掃除機のごとくゴミ溜めになってしまうのが気になるのとそんなに高負荷をかける使い方もしないのでファンをひっくり返して排気方向になるよう取り付けてみた。PWMを50℃(0%)~60℃(100%)で無段階調整する設定にしていると軽負荷ではユルユルと回り始めほぼ無音状態(耳を近づけてなんとか聞こえるレベル)で安定する。
作成したライブラリは全てのRaspberry Piで動作するよう努力したつもりではあるが試したのは4B/400/02W/0のみで他のモデルは動作するか不明。実行にはROOT権限が必要なことに注意しよう。
モデル判定のために利用したbcm_host.h/bcm_host.cについては下記公式サイトで紹介されているライブラリの一部であり余分なファイルがなくてもコンパイルできるよう一部をコメントアウトしてある。
ちなみに、おまけで作ったライブラリのdigitalWrite()の速度を試してみたら約12MHzも出る。想定外に早い...かも。
それとPWM用のクロックソースはPLLDを使用しているがモデルにより500MHzと750MHzの2種類があるのでどちらでも割り切れてPWM規定の100MHz以下の50MHzをPWMクロックとしてみた。試しに250MHzでやってみたら見事に誤動作してしまったが125MHzならいけるかも。
割り切れない周波数であってもMASHで分周するので周波数的には問題なさそうなものの補正がかかるたびにPWMクロックのエッジ・タイミングが微妙にズレてしまうので注意すべし。参考まで。
【Download】
pwmfan.zip
※/opt/pwmfan等の適当なディレクトリに展開コピーし、sudo ./make.shした後、sudo ./install.shするとsystemdに登録してくれる。
【Update】
2024-09-27
最近世の中が暑くなってきているのでもっと冷えそうなファン付きケースに交換したのはいいがファンが2線式のブラシレスモーターだったのでPWM制御できなくなってしまった。3線式のモーターに変えようかなとも思ったが合うものがなかなか見つからない。ファンは静かなので回しっぱなしでもいいけれど耐久性を考えるとやっぱり温度制御したいところだ。ただラズパイ標準のGPIOファン制御は60℃から...もっと低い温度からオンオフ制御(0% or 100%)できるよう最低温度と最高温度の設定を同じにできるようにプログラムを少し変更してみた。
例: pwmfan -c 50 -h 50
ちなみに2線式のモータを制御するには下図のようにN-MOSなどを使った簡単なモータードライバー回路が必要となる。
1 2 3 4 5 6 7 8 9 10 11 12 |
5V--------- Motor + | + Diode - | --- Motor - | _ GPIO---|_ N-MOS | GND |
このケースは肉厚があって重くていい感じ。温度の上がり方がゆっくりで軽負荷ならファンが回ることはなく室温28℃のとき44℃前後で安定する。但し、ファンは2線式なので制御するには上図のような外部回路が必要となる。
GeeekPi Raspberry Pi 4アーマーケース
Flircのケースより肉厚で重いのは良いがCPU以外のチップの放熱用突起がないのとファンがないので室温の高い部屋など環境により長時間稼働には向かなさそう。
Raspberry Pi 4B 専用 ヒートシンクケース(ブラック)【RPH-4B-H-B】
このケースが一番肉厚が薄くて温度が上がりやすい。CPU以外のチップの放熱用突起もない。長時間稼働向きではない。
Flirc ラズベリーパイ4ケース シルバー
この投稿とは関係ないけど、Pi5のケースなら次のが超お勧め。低負荷だとファンは回らないし普通サイズのNVME-SSDが使えるしSSDの放熱対策もバッチリなので安心して使える。少しお値段は高いけど満足度は高い。NVME-SSDからの起動は約6秒と爆速なのでお試しあれ。
Argon NEO 5 M.2 NVME PCIEハウジング
2022-07-31
pinmode.hの修正。他のライブラリとの互換性のためだけの修正なので動作には影響なし。
2022-07-28
pinmode.hを追加。
2022-07-21
アプリ側とライブラリ内部で同じようなPWMデューティサイクル計算が必要で二度手間だったのを解消しアプリのコードがより簡単にかけるようにanalogWrite()の引数にデューティサイクルの範囲(range)を追加。
2022-07-20
モデルにより異なるクリスタルやPLLDクロック、GPIOタイプを指定して初期化できるように改良。この改良により自動設定が対応できないモデルが出てきたとしてもアプリ側でパラメタを指定して使うことが可能となる。begin()の第一引数(xosc_freq)を省略(ゼロ指定)すると自動設定、ゼロ以外を指定すると手動設定となる。xosc_freq/plld_freqは間違った指定をするとPWMの周波数が不正になり、gpio_typeは自動判定できないときにのみ参照されるが間違った指定をするとGPIOのプルアップ/プルダウン設定が誤動作する。
int begin(uint32_t xosc_freq = 0, uint32_t plld_freq = 0, uint32_t gpio_type = 0);
xosc_freq … クリスタルクロック(Hz)
plld_freq … PLLDクロック(Hz)
gpio_type … 0=BCM2835互換仕様, 1=BCM2711(pi4)互換仕様。
2022-07-19
CPU判別がうまく動作しないせいで誤動作するモデルがあるのでCPUではなくモデルで判定するように修正。それとクリスタルの周波数を取得する方法を見つけたのでそれを元にPLLDクロックを決定するように修正。
【PWM-FAN制御プログラム】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "rpio.h" #define TEMPSENSOR "/sys/devices/virtual/thermal/thermal_zone0/temp" int temperature(void) { static int rv = 0; FILE *fp; if ((fp = fopen(TEMPSENSOR, "r"))) { char data[16]; data[fread(data, 1, sizeof(data), fp)] = 0; fclose(fp); rv = atoi(data) / 1000; } return rv; } void usage(void) { printf("Usage: pwnfan [options]\n"); printf("\n"); printf(" options: -c n ... cold temperature (*50 'C)\n"); printf(" -h n ... hot temperature (*60 'C)\n"); printf(" -p n ... PWM output pin (12/*18 or 13/19)\n"); printf(" -f n ... PWM Frequency (*10000 Hz)\n"); printf("\n"); } int main(int argc, char **argv) { int pin = 18; // hardware PWM Pin int freq = 10000; // hardware PWM frequency int cold = 50; // cold temperature int hot = 60; // hot temperature int status, temp, duty, c; printf("Cooling PWM Fan Controller Utility. Version 1.0\n"); printf("Copyright (C) 2022 Sasapea's Lab. All right reserved.\n"); printf("\n"); while ((c = getopt(argc, argv, "f:c:h:p:")) != -1) { switch (c) { case 'f': freq = atoi(optarg); break; case 'c': cold = atoi(optarg); break; case 'h': hot = atoi(optarg); break; case 'p': switch((pin = atoi(optarg))) { case 12: case 13: case 18: case 19: break; default: usage(); exit(1); } break; default: printf("\n"); usage(); exit(1); } } if (cold > hot) { usage(); exit(1); } if ((status = RPIO::begin()) < 0) { printf("Error: RPIO::begin() = %d\n", status); exit(1); } printf("board model type : %d\n", status); printf("temperature range: %d - %d\n", cold, hot); while (1) { temp = temperature(); if (temp >= hot) duty = 100; else if(temp <= cold) duty = 0; else duty = (temp - cold) * 100 / (hot - cold); RPIO::analogWrite(pin, freq, duty, 100); sleep(10); } return 0; } |
【ライブラリ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/* pinmode.h - GPIO Pin Mode Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stdint.h> #include <stdbool.h> typedef enum { INPUT = 0, OUTPUT = 1, OPEN_DRAIN = 2, OPEN_SOURCE = 3, } PinMode; typedef enum { PULL_DISABLE = 0, PULL_UP = 1, PULL_DOWN = 2, } PinPull; typedef enum { CHANGE = 0, RISING = 1, FALLING = 2, } PinEdge; typedef enum { LOW = 0, HIGH = 1, } PinStatus; typedef uint32_t pin_size_t; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
/* rpio.h - Peripheral Controll Library for Raspberry Pi Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stdint.h> #include <stdbool.h> #include "pinmode.h" #include "rpregs.h" #define PWM_BASE_CLOCK 50000000UL // 50MHz (Max 100MHz) #define PWM_DUTY_RANGE 1000UL typedef enum { PinFunc_INPUT = GPIO_FSEL_Val_INPUT, PinFunc_OUTPUT = GPIO_FSEL_Val_OUTPUT, PinFunc_0 = GPIO_FSEL_Val_ALT0, PinFunc_1 = GPIO_FSEL_Val_ALT1, PinFunc_2 = GPIO_FSEL_Val_ALT2, PinFunc_3 = GPIO_FSEL_Val_ALT3, PinFunc_4 = GPIO_FSEL_Val_ALT4, PinFunc_5 = GPIO_FSEL_Val_ALT5, } PinFunc; class RPIO { public: static int begin(uint32_t xosc_freq = 0, uint32_t plld_freq = 0, uint32_t gpio_type = 0); static void pinFunction(pin_size_t pin, PinFunc sel); static void pinPullUpDown(pin_size_t pin, PinPull pull); static void pinMode(pin_size_t pin, PinMode mode, PinPull pull = PULL_DISABLE); static void digitalWrite(pin_size_t pin, PinStatus value); static PinStatus digitalRead(pin_size_t pin); static int analogWrite(pin_size_t pin, size_t freq = 0, size_t duty = PWM_DUTY_RANGE / 2, size_t range = PWM_DUTY_RANGE, bool invert = false); static int clockOutput(pin_size_t pin, size_t freq = 0, uint32_t mash = CM_CTL_MASH_Val_MASH1); static void sleepMicroseconds(size_t usec); static void sleep(size_t msec); static void delayMicroseconds(size_t usec); static void delay(size_t msec); static size_t micros(void); static size_t millis(void); private: static void setupClock(bool enable, cm_ctldiv_t *clk, uint32_t src = 0, uint32_t mash = 0, uint32_t div = 0); static int setupPeripheral(void); static int getGpioChipType(void); static uint32_t _model_type; static uint32_t _xosc_freq; static uint32_t _plld_freq; static uint32_t _gpio_chip; static uint32_t _gpio_pins; static gpio_reg_t *_gpio_reg; static cm_reg_t *_cm_reg; static pwm_reg_t *_pwm_reg[2]; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
/* rpio.cpp - Peripheral Controll Library for Raspberry Pi Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdio.h> #include <string.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <linux/gpio.h> #include "bcm_host.h" #include "rpio.h" uint32_t RPIO::_model_type; uint32_t RPIO::_xosc_freq; uint32_t RPIO::_plld_freq; uint32_t RPIO::_gpio_chip; uint32_t RPIO::_gpio_pins = 60; gpio_reg_t *RPIO::_gpio_reg; cm_reg_t *RPIO::_cm_reg; pwm_reg_t *RPIO::_pwm_reg[]; int RPIO::begin(uint32_t xosc_freq, uint32_t plld_freq, uint32_t gpio_chip) { int rv = setupPeripheral(); if (rv != -1) { _model_type = rv = bcm_host_get_model_type(); int chiptype = getGpioChipType(); if (xosc_freq) { _xosc_freq = xosc_freq; _plld_freq = plld_freq; } else { /* * Automatic setting. * * [TODO] New model support is required */ _xosc_freq = (((CM_DIV_DIV(_cm_reg->TIMER.DIV) + 1) * 100) >> CM_DIV_DIVF_Len) * 10000; _plld_freq = (_xosc_freq == 19200000 ? 500000000 : 750000000); gpio_chip = chiptype && ((_model_type >= BCM_HOST_BOARD_TYPE_PI4MODELB) && (_model_type != BCM_HOST_BOARD_TYPE_PI02W)); } _gpio_chip = (chiptype >= 0 ? chiptype : gpio_chip); } return rv; } int RPIO::getGpioChipType(void) { int fd = open("/dev/gpiochip0", 0); if (fd != -1) { struct gpiochip_info cinfo; if (ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo) != -1) { close(fd); /* * [TODO] New model support is required */ if (strcmp("pinctrl-bcm2835", cinfo.label) == 0) { _gpio_pins = cinfo.lines; return 0; } if (strcmp("pinctrl-bcm2711", cinfo.label) == 0) { _gpio_pins = cinfo.lines; return 1; } } } return -1; } void RPIO::pinFunction(pin_size_t pin, PinFunc sel) { if (pin < _gpio_pins) { rw_reg_t *reg = &GPIO_FSEL_Reg(pin, _gpio_reg); *reg = (*reg & ~GPIO_FSEL(pin, -1)) | GPIO_FSEL(pin, sel); } } void RPIO::pinPullUpDown(pin_size_t pin, PinPull pull) { uint32_t bit; if (pin < _gpio_pins) { if (_gpio_chip) { switch (pull) { case PULL_UP: bit = GPIO_PUPDN_Val_PULLUP; break; case PULL_DOWN: bit = GPIO_PUPDN_Val_PULLDW; break; default: bit = GPIO_PUPDN_Val_DISABLE; break; } rw_reg_t *reg = &GPIO_PUPDN_Reg(pin, _gpio_reg); *reg = (*reg & ~GPIO_PUPDN(pin, -1)) | GPIO_PUPDN(pin, bit); } else { switch (pull) { case PULL_UP: bit = GPIO_PUD_Val_PULLUP; break; case PULL_DOWN: bit = GPIO_PUD_Val_PULLDW; break; default: bit = GPIO_PUD_Val_DISABLE; break; } GPIO_PUD_Reg(pin, _gpio_reg) = GPIO_PUD(pin, bit); delayMicroseconds(2); GPIO_PUDCLK_Reg(pin, _gpio_reg) = GPIO_PUDCLK(pin, 1); delayMicroseconds(2); GPIO_PUD_Reg(pin, _gpio_reg) = GPIO_PUD(pin, 0); delayMicroseconds(2); GPIO_PUDCLK_Reg(pin, _gpio_reg) = GPIO_PUDCLK(pin, 0); } } } void RPIO::pinMode(pin_size_t pin, PinMode mode, PinPull pull) { pinFunction(pin, mode == INPUT ? PinFunc_INPUT : PinFunc_OUTPUT); pinPullUpDown(pin, mode == OUTPUT ? PULL_DISABLE : pull); } void RPIO::digitalWrite(pin_size_t pin, PinStatus value) { if (pin < _gpio_pins) { if (value) GPIO_SET_Reg(pin, _gpio_reg) = GPIO_SET(pin, 1); else GPIO_CLR_Reg(pin, _gpio_reg) = GPIO_CLR(pin, 1); } } PinStatus RPIO::digitalRead(pin_size_t pin) { return pin < _gpio_pins ? (GPIO_LEV_Reg(pin, _gpio_reg) & GPIO_LEV(pin, 1) ? HIGH : LOW) : LOW; } int RPIO::analogWrite(pin_size_t pin, size_t freq, size_t duty, size_t range, bool invert) { /* Setup PWM Output Pin */ PinFunc sel = PinFunc_0; int no = 0; switch (pin) { case 18: case 19: sel = PinFunc_5; break; case 12: case 13: case 45: break; case 40: case 41: no = (_gpio_chip > 0); break; default: return -1; } pinFunction(pin, freq ? sel : PinFunc_INPUT); /* Setup PWM Clock */ const uint32_t src = CM_CTL_SRC_Val_PLLD; const uint32_t ctl = CM_CTL_SRC(src) | CM_CTL_ENAB(1); const uint32_t div = CM_DIV_DIV(((uint64_t)_plld_freq << CM_DIV_DIVF_Len) / PWM_BASE_CLOCK); cm_ctldiv_t *clk = &_cm_reg->PWM; if (((clk->CTL & (CM_CTL_SRC(-1) | CM_CTL_ENAB(-1))) != ctl) || ((clk->DIV & CM_DIV_DIV(-1)) != div)) setupClock(true, clk, src, CM_CTL_MASH_Val_MASH1, div); /* Setup PWM Controller */ if (range == 0) range = PWM_DUTY_RANGE; pwm_reg_t *pwm = _pwm_reg[no]; uint32_t rng = freq ? PWM_BASE_CLOCK / freq : 0; uint32_t dat = (uint64_t)rng * (duty >= range ? range : duty) / range; switch (pin & 1) { case 0: pwm->DAT1 = dat; delayMicroseconds(2); pwm->RNG1 = rng; delayMicroseconds(2); pwm->CTL = (pwm->CTL & ~PWM_CTL_MASK1_Val) | PWM_CTL_MSEN1(PWM_CTL_MSEN1_Val_MS) | PWM_CTL_POLA1(invert) | PWM_CTL_PWEN1(freq != 0); break; case 1: pwm->DAT2 = dat; delayMicroseconds(2); pwm->RNG2 = rng; delayMicroseconds(10); pwm->CTL = (pwm->CTL & ~PWM_CTL_MASK2_Val) | PWM_CTL_MSEN2(PWM_CTL_MSEN2_Val_MS) | PWM_CTL_POLA2(invert) | PWM_CTL_PWEN2(freq != 0); break; } return 0; } int RPIO::clockOutput(pin_size_t pin, size_t freq, uint32_t mash) { static const struct div_range { uint32_t min; uint32_t max; } DIV_RANGES[] = { { 1 << CM_DIV_DIVF_Len, (CM_DIV_DIVF_Msk - 0) << CM_DIV_DIVF_Len }, { 2 << CM_DIV_DIVF_Len, (CM_DIV_DIVF_Msk - 0) << CM_DIV_DIVF_Len }, { 3 << CM_DIV_DIVF_Len, (CM_DIV_DIVF_Msk - 1) << CM_DIV_DIVF_Len }, { 5 << CM_DIV_DIVF_Len, (CM_DIV_DIVF_Msk - 3) << CM_DIV_DIVF_Len }, }; /* Setup GP Clock Output Pin */ PinFunc sel = PinFunc_0; int no = 0; switch (pin) { case 4: case 5: case 6: no = pin - 4; break; case 20: case 21: no = pin - 20; sel = PinFunc_5; break; case 32: case 34: break; case 42: case 43: no = pin - 41; break; case 44: no = 1; break; default: return -1; } pinFunction(pin, freq ? sel : PinFunc_INPUT); /* Setup MASH */ if (mash > CM_CTL_MASH_Val_MASH3) mash = CM_CTL_MASH_Val_MASH3; const struct div_range range = DIV_RANGES[mash]; uint32_t src = CM_CTL_SRC_Val_PLLD; uint64_t div = freq ? ((uint64_t)_plld_freq << CM_DIV_DIVF_Len) / freq : 0; if (div < range.min) div = range.min; else if (div > range.max) { src = CM_CTL_SRC_Val_OSCILLATOR; div = ((uint64_t)_xosc_freq << CM_DIV_DIVF_Len) / freq; if (div > range.max) div = range.max; } /* Setup GP Clock */ setupClock(true, &CM_GP_Reg(no, _cm_reg), src, mash, div); return 0; } void RPIO::setupClock(bool enable, cm_ctldiv_t *clk, uint32_t src, uint32_t mash, uint32_t div) { clk->CTL = CM_CTL_PASSWD(CM_CTL_PASSWD_Val_WRITE) | (clk->CTL & ~CM_CTL_ENAB(1)); do delayMicroseconds(2); while (clk->CTL & CM_CTL_BUSY(1)); if (enable) { clk->DIV = CM_CTL_PASSWD(CM_DIV_PASSWD_Val_WRITE) | CM_DIV_DIV(div); delayMicroseconds(2); clk->CTL = CM_CTL_PASSWD(CM_CTL_PASSWD_Val_WRITE) | CM_CTL_MASH(mash) | CM_CTL_SRC(src) | CM_CTL_ENAB(1); } } int RPIO::setupPeripheral(void) { int fd = 0; if (!_gpio_reg) { if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) >= 0) { size_t base = bcm_host_get_peripheral_address(); size_t page = sysconf(_SC_PAGE_SIZE); _gpio_reg = (gpio_reg_t *)mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, base + GPIO_BASE_OFFSET); _cm_reg = (cm_reg_t *)mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, base + CM_BASE_OFFSET); _pwm_reg[0] = (pwm_reg_t *)mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, base + PWM0_BASE_OFFSET); _pwm_reg[1] = (pwm_reg_t *)mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, base + PWM1_BASE_OFFSET); close(fd); } } return fd; } void RPIO::sleepMicroseconds(size_t usec) { struct timespec rem, req = { tv_sec: (time_t)(usec / 1000000), tv_nsec: (long)((usec % 1000000) * 1000) }; while (nanosleep(&req, &rem) == EINTR) req = rem; } void RPIO::sleep(size_t msec) { struct timespec rem, req = { tv_sec: (time_t)(msec / 1000), tv_nsec: (long)((msec % 1000) * 1000000) }; while (nanosleep(&req, &rem) == EINTR) req = rem; } void RPIO::delayMicroseconds(size_t usec) { size_t t = micros(); while (micros() - t < usec) continue; } void RPIO::delay(size_t msec) { size_t t = millis(); while (millis() - t < msec) continue; } size_t RPIO::micros(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); return (size_t)ts.tv_sec * 1000000 + (ts.tv_nsec / 1000); } size_t RPIO::millis(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); return (size_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
/* rpregs.h - Define Peripheral Registers for Raspberry Pi Copyright (c) 2022 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #pragma once #include <stdint.h> /* Chapter 5. General Purpose I/O (GPIO) */ typedef volatile uint32_t rw_reg_t; typedef const volatile uint32_t ro_reg_t; typedef struct { rw_reg_t FSEL[6]; ro_reg_t rsv0; rw_reg_t SET[2]; ro_reg_t rsv1; rw_reg_t CLR[2]; ro_reg_t rsv2; rw_reg_t LEV[2]; ro_reg_t rsv3; rw_reg_t EDS[2]; ro_reg_t rsv4; rw_reg_t REN[2]; ro_reg_t rsv5; rw_reg_t FEN[2]; ro_reg_t rsv6; rw_reg_t HEN[2]; ro_reg_t rsv7; rw_reg_t LEN[2]; ro_reg_t rsv8; rw_reg_t AREN[2]; ro_reg_t rsv9; rw_reg_t AFEN[2]; ro_reg_t rsv10; rw_reg_t PUD; // BCM2835/2836/2837 rw_reg_t PUDCLK[2]; // BCM2835/2836/2837 ro_reg_t rsv11[17]; rw_reg_t PUPDN[4]; // BCM2711 } gpio_reg_t; #define GPIO_BASE_OFFSET 0x00200000UL #define GPIO_FSEL_Pos 0 #define GPIO_FSEL_Len 3 #define GPIO_FSEL_Cnt 10 #define GPIO_FSEL_Msk 0x07 #define GPIO_FSEL_Val_INPUT 0 #define GPIO_FSEL_Val_OUTPUT 1 #define GPIO_FSEL_Val_ALT0 4 #define GPIO_FSEL_Val_ALT1 5 #define GPIO_FSEL_Val_ALT2 6 #define GPIO_FSEL_Val_ALT3 7 #define GPIO_FSEL_Val_ALT4 3 #define GPIO_FSEL_Val_ALT5 2 #define GPIO_FSEL_Reg(pin, reg) ((reg)->FSEL[(pin) / GPIO_FSEL_Cnt]) #define GPIO_FSEL(pin, val) (((val) & GPIO_FSEL_Msk) << (((pin) % GPIO_FSEL_Cnt) * GPIO_FSEL_Len)) #define GPIO_SET_Pos 0 #define GPIO_SET_Len 1 #define GPIO_SET_Cnt 32 #define GPIO_SET_Msk 0x01 #define GPIO_SET_Reg(pin, reg) ((reg)->SET[(pin) / GPIO_SET_Cnt]) #define GPIO_SET(pin, val) (((val) & GPIO_SET_Msk) << (((pin) % GPIO_SET_Cnt) * GPIO_SET_Len)) #define GPIO_CLR_Pos 0 #define GPIO_CLR_Len 1 #define GPIO_CLR_Cnt 32 #define GPIO_CLR_Msk 0x01 #define GPIO_CLR_Reg(pin, reg) ((reg)->CLR[(pin) / GPIO_CLR_Cnt]) #define GPIO_CLR(pin, val) (((val) & GPIO_CLR_Msk) << (((pin) % GPIO_CLR_Cnt) * GPIO_CLR_Len)) #define GPIO_LEV_Pos 0 #define GPIO_LEV_Len 1 #define GPIO_LEV_Cnt 32 #define GPIO_LEV_Msk 0x01 #define GPIO_LEV_Reg(pin, reg) ((reg)->LEV[(pin) / GPIO_LEV_Cnt]) #define GPIO_LEV(pin, val) (((val) & GPIO_LEV_Msk) << (((pin) % GPIO_LEV_Cnt) * GPIO_LEV_Len)) #define GPIO_EDS_Pos 0 #define GPIO_EDS_Len 1 #define GPIO_EDS_Cnt 32 #define GPIO_EDS_Msk 0x01 #define GPIO_EDS_Reg(pin, reg) ((reg)->EDS[(pin) / GPIO_EDS_Cnt]) #define GPIO_EDS(pin, val) (((val) & GPIO_EDS_Msk) << (((pin) % GPIO_EDS_Cnt) * GPIO_EDS_Len)) #define GPIO_REN_Pos 0 #define GPIO_REN_Len 1 #define GPIO_REN_Cnt 32 #define GPIO_REN_Msk 0x01 #define GPIO_REN_Reg(pin, reg) ((reg)->REN[(pin) / GPIO_REN_Cnt]) #define GPIO_REN(pin, val) (((val) & GPIO_REN_Msk) << (((pin) % GPIO_REN_Cnt) * GPIO_REN_Len)) #define GPIO_FEN_Pos 0 #define GPIO_FEN_Len 1 #define GPIO_FEN_Cnt 32 #define GPIO_FEN_Msk 0x01 #define GPIO_FEN_Reg(pin, reg) ((reg)->FEN[(pin) / GPIO_FEN_Cnt]) #define GPIO_FEN(pin, val) (((val) & GPIO_FEN_Msk) << (((pin) % GPIO_FEN_Cnt) * GPIO_FEN_Len)) #define GPIO_HEN_Pos 0 #define GPIO_HEN_Len 1 #define GPIO_HEN_Cnt 32 #define GPIO_HEN_Msk 0x01 #define GPIO_HEN_Reg(pin, reg) ((reg)->HEN[(pin) / GPIO_HEN_Cnt]) #define GPIO_HEN(pin, val) (((val) & GPIO_HEN_Msk) << (((pin) % GPIO_HEN_Cnt) * GPIO_HEN_Len)) #define GPIO_LEN_Pos 0 #define GPIO_LEN_Len 1 #define GPIO_LEN_Cnt 32 #define GPIO_LEN_Msk 0x01 #define GPIO_LEN_Reg(pin, reg) ((reg)->LEN[(pin) / GPIO_LEN_Cnt]) #define GPIO_LEN(pin, val) (((val) & GPIO_LEN_Msk) << (((pin) % GPIO_LEN_Cnt) * GPIO_LEN_Len)) #define GPIO_AREN_Pos 0 #define GPIO_AREN_Len 1 #define GPIO_AREN_Cnt 32 #define GPIO_AREN_Msk 0x01 #define GPIO_AREN_Reg(pin, reg) ((reg)->AREN[(pin) / GPIO_AREN_Cnt]) #define GPIO_AREN(pin, val) (((val) & GPIO_AREN_Msk) << (((pin) % GPIO_AREN_Cnt) * GPIO_AREN_Len)) #define GPIO_AFEN_Pos 0 #define GPIO_AFEN_Len 1 #define GPIO_AFEN_Cnt 32 #define GPIO_AFEN_Msk 0x01 #define GPIO_AFEN_Reg(pin, reg) ((reg)->AFEN[(pin) / GPIO_AFEN_Cnt]) #define GPIO_AFEN(pin, val) (((val) & GPIO_AFEN_Msk) << (((pin) % GPIO_AFEN_Cnt) * GPIO_AFEN_Len)) #define GPIO_PUD_Pos 0 #define GPIO_PUD_Len 2 #define GPIO_PUD_Cnt 1 #define GPIO_PUD_Msk 0x03 #define GPIO_PUD_Val_DISABLE 0 #define GPIO_PUD_Val_PULLDW 1 #define GPIO_PUD_Val_PULLUP 2 #define GPIO_PUD_Reg(pin, reg) ((reg)->PUD) #define GPIO_PUD(pin, val) (((val) & GPIO_PUD_Msk) << GPIO_PUD_Pos) #define GPIO_PUDCLK_Pos 0 #define GPIO_PUDCLK_Len 1 #define GPIO_PUDCLK_Cnt 32 #define GPIO_PUDCLK_Msk 0x01 #define GPIO_PUDCLK_Reg(pin, reg) ((reg)->PUDCLK[(pin) / GPIO_PUDCLK_Cnt]) #define GPIO_PUDCLK(pin, val) (((val) & GPIO_PUDCLK_Msk) << (((pin) % GPIO_PUDCLK_Cnt) * GPIO_PUDCLK_Len)) #define GPIO_PUPDN_Pos 0 #define GPIO_PUPDN_Len 2 #define GPIO_PUPDN_Cnt 16 #define GPIO_PUPDN_Msk 0x03 #define GPIO_PUPDN_Val_DISABLE 0 #define GPIO_PUPDN_Val_PULLUP 1 #define GPIO_PUPDN_Val_PULLDW 2 #define GPIO_PUPDN_Reg(pin, reg) ((reg)->PUPDN[(pin) / GPIO_PUPDN_Cnt]) #define GPIO_PUPDN(pin, val) (((val) & GPIO_PUPDN_Msk) << (((pin) % GPIO_PUPDN_Cnt) * GPIO_PUPDN_Len)) /* 5.4. General Purpose GPIO Clocks */ typedef struct { rw_reg_t CTL; rw_reg_t DIV; } cm_ctldiv_t; typedef struct { cm_ctldiv_t GNRI; // 0x000 cm_ctldiv_t VPU; // 0x008 cm_ctldiv_t SYS; // 0x010 cm_ctldiv_t PERIA; // 0x018 cm_ctldiv_t PERII; // 0x020 cm_ctldiv_t H264; // 0x028 cm_ctldiv_t ISP; // 0x030 cm_ctldiv_t V3D; // 0x038 cm_ctldiv_t CAM0; // 0x040 cm_ctldiv_t CAM1; // 0x048 cm_ctldiv_t CCP2; // 0x050 cm_ctldiv_t DSI0E; // 0x058 cm_ctldiv_t DSI0P; // 0x060 cm_ctldiv_t DPI; // 0x068 cm_ctldiv_t GP[3]; // 0x070 cm_ctldiv_t HSM; // 0x088 cm_ctldiv_t OTP; // 0x090 cm_ctldiv_t PCM; // 0x098 cm_ctldiv_t PWM; // 0x0a0 cm_ctldiv_t SLIM; // 0x0a8 cm_ctldiv_t SMI; // 0x0b0 cm_ctldiv_t rsv1; cm_ctldiv_t TCNT; // 0x0c0 cm_ctldiv_t TEC; // 0x0c8 cm_ctldiv_t TD0; // 0x0d0 cm_ctldiv_t TD1; // 0x0d8 cm_ctldiv_t TSENS; // 0x0e0 cm_ctldiv_t TIMER; // 0x0e8 cm_ctldiv_t UART; // 0x0f0 cm_ctldiv_t VEC; // 0x0f8 cm_ctldiv_t rsv2[18]; cm_ctldiv_t PULSE; // 0x190 cm_ctldiv_t rsv3[2]; cm_ctldiv_t SDC; // 0x1a8 cm_ctldiv_t ARM; // 0x1b0 cm_ctldiv_t AVEO; // 0x1b8 cm_ctldiv_t EMMC; // 0x1c0 cm_ctldiv_t rsv4; // 0x1c8 cm_ctldiv_t EMMC2; // 0x1d0 } cm_reg_t; #define CM_BASE_OFFSET 0x00101000UL #define CM_GP_CHANNELS 3 #define CM_GP_Reg(channel, reg) ((reg)->GP[channel]) #define CM_CTL_SRC_Pos 0 #define CM_CTL_SRC_Len 4 #define CM_CTL_SRC_Cnt 1 #define CM_CTL_SRC_Msk 0x0F #define CM_CTL_SRC_Val_GND 0 #define CM_CTL_SRC_Val_OSCILLATOR 1 // 19.2MHz #define CM_CTL_SRC_Val_TESTDEBUG0 2 #define CM_CTL_SRC_Val_TESTDEBUG1 3 #define CM_CTL_SRC_Val_PLLA 4 // 0MHz ? #define CM_CTL_SRC_Val_PLLC 5 // 1000MHz ? #define CM_CTL_SRC_Val_PLLD 6 // 500MHz #define CM_CTL_SRC_Val_HDMI 7 // 216MHz #define CM_CTL_SRC(val) (((val) & CM_CTL_SRC_Msk) << CM_CTL_SRC_Pos) #define CM_CTL_ENAB_Pos 4 #define CM_CTL_ENAB_Len 1 #define CM_CTL_ENAB_Cnt 1 #define CM_CTL_ENAB_Msk 0x01 #define CM_CTL_ENAB(val) (((val) & CM_CTL_ENAB_Msk) << CM_CTL_ENAB_Pos) #define CM_CTL_KILL_Pos 5 #define CM_CTL_KILL_Len 1 #define CM_CTL_KILL_Cnt 1 #define CM_CTL_KILL_Msk 0x01 #define CM_CTL_KILL(val) (((val) & CM_CTL_KILL_Msk) << CM_CTL_KILL_Pos) #define CM_CTL_BUSY_Pos 7 #define CM_CTL_BUSY_Len 1 #define CM_CTL_BUSY_Cnt 1 #define CM_CTL_BUSY_Msk 0x01 #define CM_CTL_BUSY(val) (((val) & CM_CTL_BUSY_Msk) << CM_CTL_BUSY_Pos) #define CM_CTL_FLIP_Pos 8 #define CM_CTL_FLIP_Len 1 #define CM_CTL_FLIP_Cnt 1 #define CM_CTL_FLIP_Msk 0x01 #define CM_CTL_FLIP(val) (((val) & CM_CTL_FLIP_Msk) << CM_CTL_FLIP_Pos) #define CM_CTL_MASH_Pos 9 #define CM_CTL_MASH_Len 2 #define CM_CTL_MASH_Cnt 1 #define CM_CTL_MASH_Msk 0x03 #define CM_CTL_MASH_Val_INTERGER 0 #define CM_CTL_MASH_Val_MASH1 1 #define CM_CTL_MASH_Val_MASH2 2 #define CM_CTL_MASH_Val_MASH3 3 #define CM_CTL_MASH(val) (((val) & CM_CTL_MASH_Msk) << CM_CTL_MASH_Pos) #define CM_CTL_PASSWD_Pos 24 #define CM_CTL_PASSWD_Len 8 #define CM_CTL_PASSWD_Cnt 1 #define CM_CTL_PASSWD_Msk 0xFF #define CM_CTL_PASSWD_Val_WRITE 0x5A #define CM_CTL_PASSWD(val) (((val) & CM_CTL_PASSWD_Msk) << CM_CTL_PASSWD_Pos) #define CM_DIV_DIVF_Pos 0 #define CM_DIV_DIVF_Len 12 #define CM_DIV_DIVF_Cnt 1 #define CM_DIV_DIVF_Msk 0x0FFF #define CM_DIV_DIVF(val) (((val) & CM_DIV_DIVF_Msk) << CM_DIV_DIVF_Pos) #define CM_DIV_DIVI_Pos 12 #define CM_DIV_DIVI_Len 12 #define CM_DIV_DIVI_Cnt 1 #define CM_DIV_DIVI_Msk 0x0FFF #define CM_DIV_DIVI(val) (((val) & CM_DIV_DIVI_Msk) << CM_DIV_DIVI_Pos) #define CM_DIV_DIV_Pos 0 #define CM_DIV_DIV_Len 24 #define CM_DIV_DIV_Cnt 1 #define CM_DIV_DIV_Msk 0xFFFFFF #define CM_DIV_DIV(val) (((val) & CM_DIV_DIV_Msk) << CM_DIV_DIV_Pos) #define CM_DIV_PASSWD_Pos 24 #define CM_DIV_PASSWD_Len 8 #define CM_DIV_PASSWD_Cnt 1 #define CM_DIV_PASSWD_Msk 0xFF #define CM_DIV_PASSWD_Val_WRITE 0x5A #define CM_DIV_PASSWD(val) (((val) & CM_DIV_PASSWD_Msk) << CM_DIV_PASSWD_Pos) /* Chapter 8. Pulse Width Modulator */ typedef struct { rw_reg_t CTL; rw_reg_t STA; rw_reg_t DMAC; ro_reg_t rsv0; rw_reg_t RNG1; rw_reg_t DAT1; rw_reg_t FIF1; ro_reg_t rsv1; rw_reg_t RNG2; rw_reg_t DAT2; } pwm_reg_t; #define PWM0_BASE_OFFSET 0x0020c000UL #define PWM1_BASE_OFFSET 0x0020c800UL #define PWM_MAX_CHANNELS 2 #define PWM_CTL_MASK1_Val 0x00FF #define PWM_CTL_MASK2_Val 0xFF00 #define PWM_CTL_PWEN1_Pos 0 #define PWM_CTL_PWEN1_Len 1 #define PWM_CTL_PWEN1_Cnt 1 #define PWM_CTL_PWEN1_Msk 0x01 #define PWM_CTL_PWEN1_Val_DISABLE 0 #define PWM_CTL_PWEN1_Val_ENABLE 1 #define PWM_CTL_PWEN1(val) (((val) & PWM_CTL_PWEN1_Msk) << PWM_CTL_PWEN1_Pos) #define PWM_CTL_MODE1_Pos 1 #define PWM_CTL_MODE1_Len 1 #define PWM_CTL_MODE1_Cnt 1 #define PWM_CTL_MODE1_Msk 0x01 #define PWM_CTL_MODE1_Val_PWM 0 #define PWM_CTL_MODE1_Val_SERIALISER 1 #define PWM_CTL_MODE1(val) (((val) & PWM_CTL_MODE1_Msk) << PWM_CTL_MODE1_Pos) #define PWM_CTL_RPTL1_Pos 2 #define PWM_CTL_RPTL1_Len 1 #define PWM_CTL_RPTL1_Cnt 1 #define PWM_CTL_RPTL1_Msk 0x01 #define PWM_CTL_RPTL1_Val_PWM 0 #define PWM_CTL_RPTL1_Val_SERIALISER 1 #define PWM_CTL_RPTL1(val) (((val) & PWM_CTL_RPTL1_Msk) << PWM_CTL_RPTL1_Pos) #define PWM_CTL_SBIT1_Pos 3 #define PWM_CTL_SBIT1_Len 1 #define PWM_CTL_SBIT1_Cnt 1 #define PWM_CTL_SBIT1_Msk 0x01 #define PWM_CTL_SBIT1_Val_LOW 0 #define PWM_CTL_SBIT1_Val_HIGH 1 #define PWM_CTL_SBIT1(val) (((val) & PWM_CTL_SBIT1_Msk) << PWM_CTL_SBIT1_Pos) #define PWM_CTL_POLA1_Pos 4 #define PWM_CTL_POLA1_Len 1 #define PWM_CTL_POLA1_Cnt 1 #define PWM_CTL_POLA1_Msk 0x01 #define PWM_CTL_POLA1_Val_LOW 0 #define PWM_CTL_POLA1_Val_HIGH 1 #define PWM_CTL_POLA1(val) (((val) & PWM_CTL_POLA1_Msk) << PWM_CTL_POLA1_Pos) #define PWM_CTL_USEF1_Pos 5 #define PWM_CTL_USEF1_Len 1 #define PWM_CTL_USEF1_Cnt 1 #define PWM_CTL_USEF1_Msk 0x01 #define PWM_CTL_USEF1_Val_DATA 0 #define PWM_CTL_USEF1_Val_FIFO 1 #define PWM_CTL_USEF1(val) (((val) & PWM_CTL_USEF1_Msk) << PWM_CTL_USEF1_Pos) #define PWM_CTL_CLRF1_Pos 6 #define PWM_CTL_CLRF1_Len 1 #define PWM_CTL_CLRF1_Cnt 1 #define PWM_CTL_CLRF1_Msk 0x01 #define PWM_CTL_CLRF1(val) (((val) & PWM_CTL_CLRF1_Msk) << PWM_CTL_CLRF1_Pos) #define PWM_CTL_MSEN1_Pos 7 #define PWM_CTL_MSEN1_Len 1 #define PWM_CTL_MSEN1_Cnt 1 #define PWM_CTL_MSEN1_Msk 0x01 #define PWM_CTL_MSEN1_Val_PWM 0 #define PWM_CTL_MSEN1_Val_MS 1 #define PWM_CTL_MSEN1(val) (((val) & PWM_CTL_MSEN1_Msk) << PWM_CTL_MSEN1_Pos) #define PWM_CTL_PWEN2_Pos 8 #define PWM_CTL_PWEN2_Len 1 #define PWM_CTL_PWEN2_Cnt 1 #define PWM_CTL_PWEN2_Msk 0x01 #define PWM_CTL_PWEN2_Val_DISABLE 0 #define PWM_CTL_PWEN2_Val_ENABLE 1 #define PWM_CTL_PWEN2(val) (((val) & PWM_CTL_PWEN2_Msk) << PWM_CTL_PWEN2_Pos) #define PWM_CTL_MODE2_Pos 9 #define PWM_CTL_MODE2_Len 1 #define PWM_CTL_MODE2_Cnt 1 #define PWM_CTL_MODE2_Msk 0x01 #define PWM_CTL_MODE2_Val_PWM 0 #define PWM_CTL_MODE2_Val_SERIALISER 1 #define PWM_CTL_MODE2(val) (((val) & PWM_CTL_MODE2_Msk) << PWM_CTL_MODE2_Pos) #define PWM_CTL_RPTL2_Pos 10 #define PWM_CTL_RPTL2_Len 1 #define PWM_CTL_RPTL2_Cnt 1 #define PWM_CTL_RPTL2_Msk 0x01 #define PWM_CTL_RPTL2_Val_PWM 0 #define PWM_CTL_RPTL2_Val_SERIALISER 1 #define PWM_CTL_RPTL2(val) (((val) & PWM_CTL_RPTL2_Msk) << PWM_CTL_RPTL2_Pos) #define PWM_CTL_SBIT2_Pos 11 #define PWM_CTL_SBIT2_Len 1 #define PWM_CTL_SBIT2_Cnt 1 #define PWM_CTL_SBIT2_Msk 0x01 #define PWM_CTL_SBIT2_Val_LOW 0 #define PWM_CTL_SBIT2_Val_HIGH 1 #define PWM_CTL_SBIT2(val) (((val) & PWM_CTL_SBIT2_Msk) << PWM_CTL_SBIT2_Pos) #define PWM_CTL_POLA2_Pos 12 #define PWM_CTL_POLA2_Len 1 #define PWM_CTL_POLA2_Cnt 1 #define PWM_CTL_POLA2_Msk 0x01 #define PWM_CTL_POLA2_Val_LOW 0 #define PWM_CTL_POLA2_Val_HIGH 1 #define PWM_CTL_POLA2(val) (((val) & PWM_CTL_POLA2_Msk) << PWM_CTL_POLA2_Pos) #define PWM_CTL_USEF2_Pos 13 #define PWM_CTL_USEF2_Len 1 #define PWM_CTL_USEF2_Cnt 1 #define PWM_CTL_USEF2_Msk 0x01 #define PWM_CTL_USEF2_Val_DATA 0 #define PWM_CTL_USEF2_Val_FIFO 1 #define PWM_CTL_USEF2(val) (((val) & PWM_CTL_USEF2_Msk) << PWM_CTL_USEF2_Pos) #define PWM_CTL_MSEN2_Pos 15 #define PWM_CTL_MSEN2_Len 1 #define PWM_CTL_MSEN2_Cnt 1 #define PWM_CTL_MSEN2_Msk 0x01 #define PWM_CTL_MSEN2_Val_PWM 0 #define PWM_CTL_MSEN2_Val_MS 1 #define PWM_CTL_MSEN2(val) (((val) & PWM_CTL_MSEN2_Msk) << PWM_CTL_MSEN2_Pos) #define PWM_STA_FULL1_Pos 0 #define PWM_STA_FULL1_Len 1 #define PWM_STA_FULL1_Cnt 1 #define PWM_STA_FULL1_Msk 0x01 #define PWM_STA_FULL1(val) (((val) & PWM_STA_FULL1_Msk) << PWM_STA_FULL1_Pos) #define PWM_STA_EMPT1_Pos 1 #define PWM_STA_EMPT1_Len 1 #define PWM_STA_EMPT1_Cnt 1 #define PWM_STA_EMPT1_Msk 0x01 #define PWM_STA_EMPT1(val) (((val) & PWM_STA_EMPT1_Msk) << PWM_STA_EMPT1_Pos) #define PWM_STA_WERR1_Pos 2 #define PWM_STA_WERR1_Len 1 #define PWM_STA_WERR1_Cnt 1 #define PWM_STA_WERR1_Msk 0x01 #define PWM_STA_WERR1(val) (((val) & PWM_STA_WERR1_Msk) << PWM_STA_WERR1_Pos) #define PWM_STA_RERR1_Pos 3 #define PWM_STA_RERR1_Len 1 #define PWM_STA_RERR1_Cnt 1 #define PWM_STA_RERR1_Msk 0x01 #define PWM_STA_RERR1(val) (((val) & PWM_STA_RERR1_Msk) << PWM_STA_RERR1_Pos) #define PWM_STA_GAPO1_Pos 4 #define PWM_STA_GAPO1_Len 1 #define PWM_STA_GAPO1_Cnt 1 #define PWM_STA_GAPO1_Msk 0x01 #define PWM_STA_GAPO1(val) (((val) & PWM_STA_GAPO1_Msk) << PWM_STA_GAPO1_Pos) #define PWM_STA_GAPO2_Pos 5 #define PWM_STA_GAPO2_Len 1 #define PWM_STA_GAPO2_Cnt 1 #define PWM_STA_GAPO2_Msk 0x01 #define PWM_STA_GAPO2(val) (((val) & PWM_STA_GAPO2_Msk) << PWM_STA_GAPO2_Pos) #define PWM_STA_BERR_Pos 8 #define PWM_STA_BERR_Len 1 #define PWM_STA_BERR_Cnt 1 #define PWM_STA_BERR_Msk 0x01 #define PWM_STA_BERR(val) (((val) & PWM_STA_BERR_Msk) << PWM_STA_BERR_Pos) #define PWM_STA_STA1_Pos 9 #define PWM_STA_STA1_Len 1 #define PWM_STA_STA1_Cnt 1 #define PWM_STA_STA1_Msk 0x01 #define PWM_STA_STA1(val) (((val) & PWM_STA_STA1_Msk) << PWM_STA_STA1_Pos) #define PWM_STA_STA2_Pos 10 #define PWM_STA_STA2_Len 1 #define PWM_STA_STA2_Cnt 1 #define PWM_STA_STA2_Msk 0x01 #define PWM_STA_STA2(val) (((val) & PWM_STA_STA2_Msk) << PWM_STA_STA2_Pos) #define PWM_DMAC_DREQ_Pos 0 #define PWM_DMAC_DREQ_Len 8 #define PWM_DMAC_DREQ_Cnt 1 #define PWM_DMAC_DREQ_Msk 0xFF #define PWM_DMAC_DREQ(val) (((val) & PWM_DMAC_DREQ_Msk) << PWM_DMAC_DREQ_Pos) #define PWM_DMAC_PANIC_Pos 8 #define PWM_DMAC_PANIC_Len 8 #define PWM_DMAC_PANIC_Cnt 1 #define PWM_DMAC_PANIC_Msk 0xFF #define PWM_DMAC_PANIC(val) (((val) & PWM_DMAC_PANIC_Msk) << PWM_DMAC_PANIC_Pos) #define PWM_DMAC_ENAB_Pos 31 #define PWM_DMAC_ENAB_Len 1 #define PWM_DMAC_ENAB_Cnt 1 #define PWM_DMAC_ENAB_Msk 0xFF #define PWM_DMAC_ENAB(val) (((val) & PWM_DMAC_ENAB_Msk) << PWM_DMAC_ENAB_Pos) #define PWM_RNG_RANGE_Pos 0 #define PWM_RNG_RANGE_Len 32 #define PWM_RNG_RANGE_Cnt 1 #define PWM_RNG_RANGE_Msk 0xFFFFFFFF #define PWM_RNG_RANGE(val) (((val) & PWM_RNG_RANGE_Msk) << PWM_RNG_RANGE_Pos) #define PWM_DATA_DATA_Pos 0 #define PWM_DATA_DATA_Len 32 #define PWM_DATA_DATA_Cnt 1 #define PWM_DATA_DATA_Msk 0xFFFFFFFF #define PWM_DATA_DATA(val) (((val) & PWM_DATA_DATA_Msk) << PWM_DATA_DATA_Pos) #define PWM_FIF1_FIFO_Pos 0 #define PWM_FIF1_FIFO_Len 32 #define PWM_FIF1_FIFO_Cnt 1 #define PWM_FIF1_FIFO_Msk 0xFFFFFFFF #define PWM_FIF1_FIFO(val) (((val) & PWM_FIF1_FIFO_Msk) << PWM_FIF1_FIFO_Pos) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Header file with useful bits from other headers #ifndef BCM_HOST_H #define BCM_HOST_H #include <stdint.h> #ifdef __cplusplus extern "C" { #endif #if 0 // commentout. Sasapea's Lab #include "interface/vmcs_host/vc_dispmanx.h" #include "interface/vmcs_host/vc_tvservice.h" #include "interface/vmcs_host/vc_cec.h" #include "interface/vmcs_host/vc_cecservice.h" #include "interface/vmcs_host/vcgencmd.h" void bcm_host_init(void); void bcm_host_deinit(void); int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height); #endif unsigned bcm_host_get_peripheral_address(void); unsigned bcm_host_get_peripheral_size(void); unsigned bcm_host_get_sdram_address(void); /* Returns the type of the Pi being used */ #define BCM_HOST_BOARD_TYPE_MODELA 0 #define BCM_HOST_BOARD_TYPE_MODELB 1 #define BCM_HOST_BOARD_TYPE_MODELAPLUS 2 #define BCM_HOST_BOARD_TYPE_MODELBPLUS 3 #define BCM_HOST_BOARD_TYPE_PI2MODELB 4 #define BCM_HOST_BOARD_TYPE_ALPHA 5 #define BCM_HOST_BOARD_TYPE_CM 6 #define BCM_HOST_BOARD_TYPE_CM2 7 #define BCM_HOST_BOARD_TYPE_PI3MODELB 8 #define BCM_HOST_BOARD_TYPE_PI0 9 #define BCM_HOST_BOARD_TYPE_CM3 0xa #define BCM_HOST_BOARD_TYPE_CUSTOM 0xb #define BCM_HOST_BOARD_TYPE_PI0W 0xc #define BCM_HOST_BOARD_TYPE_PI3MODELBPLUS 0xd #define BCM_HOST_BOARD_TYPE_PI3MODELAPLUS 0xe #define BCM_HOST_BOARD_TYPE_FPGA 0xf #define BCM_HOST_BOARD_TYPE_CM3PLUS 0x10 #define BCM_HOST_BOARD_TYPE_PI4MODELB 0x11 #define BCM_HOST_BOARD_TYPE_PI02W 0x12 #define BCM_HOST_BOARD_TYPE_PI400 0x13 #define BCM_HOST_BOARD_TYPE_CM4 0x14 extern int bcm_host_get_model_type(void); /* Returns 1 if model is Pi4 */ extern int bcm_host_is_model_pi4(void); /* Returns 1 if fkms is active (dtoverlay=v3d-fkms-vc4) */ extern int bcm_host_is_fkms_active(void); /* Returns 1 if kms is active (dtoverlay=v3d-kms-vc4) */ extern int bcm_host_is_kms_active(void); /* returns the processor ID */ #define BCM_HOST_PROCESSOR_BCM2835 0 #define BCM_HOST_PROCESSOR_BCM2836 1 #define BCM_HOST_PROCESSOR_BCM2837 2 #define BCM_HOST_PROCESSOR_BCM2838 3 extern int bcm_host_get_processor_id(void); #ifdef __cplusplus } #endif #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <string.h> #include "bcm_host.h" #if 0 // commentout. Sasapea's Lab #include "include/bcm_host.h" #include "interface/vmcs_host/vc_dispmanx.h" #include "interface/vmcs_host/vc_vchi_gencmd.h" #include "interface/vmcs_host/vc_vchi_bufman.h" #include "interface/vmcs_host/vc_tvservice.h" #include "interface/vmcs_host/vc_cecservice.h" #include "interface/vchiq_arm/vchiq_if.h" static VCHI_INSTANCE_T global_initialise_instance; static VCHI_CONNECTION_T *global_connection; int32_t graphics_get_display_size( const uint16_t display_number, uint32_t *width, uint32_t *height) { DISPMANX_DISPLAY_HANDLE_T display_handle = 0; DISPMANX_MODEINFO_T mode_info; int32_t success = -1; // Display must be opened first. display_handle = vc_dispmanx_display_open(display_number); if (display_handle) { success = vc_dispmanx_display_get_info(display_handle, &mode_info); if( success >= 0 ) { if( NULL != width ) { *width = mode_info.width; } if( NULL != height ) { *height = mode_info.height; } } vc_dispmanx_display_close(display_handle); display_handle = 0; } return success; } void host_app_message_handler(void) { printf("host_app_message_handler\n"); } void vc_host_get_vchi_state(VCHI_INSTANCE_T *initialise_instance, VCHI_CONNECTION_T **connection) { if (initialise_instance) *initialise_instance = global_initialise_instance; if (connection) *connection = global_connection; } void bcm_host_init(void) { VCHIQ_INSTANCE_T vchiq_instance; static int initted; int success = -1; char response[ 128 ]; if (initted) return; initted = 1; vcos_init(); if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) { printf("* failed to open vchiq instance\n"); exit(-1); } vcos_log("vchi_initialise"); success = vchi_initialise( &global_initialise_instance); vcos_assert(success == 0); vchiq_instance = (VCHIQ_INSTANCE_T)global_initialise_instance; global_connection = vchi_create_connection(single_get_func_table(), vchi_mphi_message_driver_func_table()); vcos_log("vchi_connect"); vchi_connect(&global_connection, 1, global_initialise_instance); vc_vchi_gencmd_init (global_initialise_instance, &global_connection, 1); vc_vchi_dispmanx_init (global_initialise_instance, &global_connection, 1); vc_vchi_tv_init (global_initialise_instance, &global_connection, 1); vc_vchi_cec_init (global_initialise_instance, &global_connection, 1); //vc_vchi_bufman_init (global_initialise_instance, &global_connection, 1); if ( success == 0 ) { success = vc_gencmd( response, sizeof(response), "set_vll_dir /sd/vlls" ); vcos_assert( success == 0 ); } } void bcm_host_deinit(void) { } // Fix linking problems. These are referenced by libs, but shouldn't be called void wfc_stream_await_buffer(void * stream) { vcos_assert(0); } #endif static unsigned get_dt_ranges(const char *filename, unsigned offset) { unsigned address = ~0; FILE *fp = fopen(filename, "rb"); if (fp) { unsigned char buf[4]; fseek(fp, offset, SEEK_SET); if (fread(buf, 1, sizeof buf, fp) == sizeof buf) address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; fclose(fp); } return address; } unsigned bcm_host_get_peripheral_address(void) { unsigned address = get_dt_ranges("/proc/device-tree/soc/ranges", 4); if (address == 0) address = get_dt_ranges("/proc/device-tree/soc/ranges", 8); return address == ~0 ? 0x20000000 : address; } unsigned bcm_host_get_peripheral_size(void) { unsigned address = get_dt_ranges("/proc/device-tree/soc/ranges", 4); address = get_dt_ranges("/proc/device-tree/soc/ranges", (address == 0) ? 12 : 8); return address == ~0 ? 0x01000000 : address; } unsigned bcm_host_get_sdram_address(void) { unsigned address = get_dt_ranges("/proc/device-tree/axi/vc_mem/reg", 8); return address == ~0 ? 0x40000000 : address; } static int read_string_from_file(const char *filename, const char *format, unsigned int *value) { FILE *fin; char str[256]; int found = 0; fin = fopen(filename, "rt"); if (fin == NULL) return 0; while (fgets(str, sizeof(str), fin) != NULL) { if (value) { if (sscanf(str, format, value) == 1) { found = 1; break; } } else { if (!strcmp(str, format)) { found = 1; break; } } } fclose(fin); return found; } static unsigned int get_revision_code() { static unsigned int revision_num = -1; unsigned int num; if (revision_num == -1 && read_string_from_file("/proc/cpuinfo", "Revision : %x", &num)) revision_num = num; return revision_num; } /* Returns the type of the Pi being used */ int bcm_host_get_model_type(void) { static int model_type = -1; if (model_type != -1) return model_type; unsigned int revision_num = get_revision_code(); if (!revision_num) model_type = 0; // Check for old/new style revision code. Bit 23 will be guaranteed one for new style else if (revision_num & 0x800000) model_type = (revision_num & 0xff0) >> 4; else { // Mask off warrantee and overclock bits. revision_num &= 0xffffff; // Map old style to new Type code if (revision_num < 2 || revision_num > 21) return 0; static const unsigned char type_map[] = { 1, // B rev 1.0 2 1, // B rev 1.0 3 1, // B rev 2.0 4 1, // B rev 2.0 5 1, // B rev 2.0 6 0, // A rev 2 7 0, // A rev 2 8 0, // A rev 2 9 0, 0, 0, // unused a,b,c 1, // B rev 2.0 d 1, // B rev 2.0 e 1, // B rev 2.0 f 3, // B+ rev 1.2 10 6, // CM1 11 2, // A+ rev1.1 12 3, // B+ rev 1.2 13 6, // CM1 14 2 // A+ 15 }; model_type = type_map[revision_num-2]; } return model_type; } /* Returns the type of the Pi being used */ int bcm_host_is_model_pi4(void) { return bcm_host_get_model_type() == 0x11 ? 1 : 0; } /* returns the processor ID */ int bcm_host_get_processor_id(void) { unsigned int revision_num = get_revision_code(); if (revision_num & 0x800000) { return (revision_num & 0xf000) >> 12; } else { // Old style number only used 2835 return BCM_HOST_PROCESSOR_BCM2835; } } static int bcm_host_is_fkms_or_kms_active(int kms) { if (!read_string_from_file("/proc/device-tree/soc/v3d@7ec00000/status", "okay", NULL) && !read_string_from_file("/proc/device-tree/v3dbus/v3d@7ec04000/status", "okay", NULL)) return 0; else return read_string_from_file("/proc/device-tree/soc/firmwarekms@7e600000/status", "okay", NULL) ^ kms; } int bcm_host_is_fkms_active(void) { static int active = -1; if (active == -1) active = bcm_host_is_fkms_or_kms_active(0); return active; } int bcm_host_is_kms_active(void) { static int active = -1; if (active == -1) active = bcm_host_is_fkms_or_kms_active(1); return active; } |