products:mpino_16a8r8t
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
| 양쪽 이전 판이전 판다음 판 | 이전 판 | ||
| products:mpino_16a8r8t [2026/09/03 14:21] – 바깥 편집 127.0.0.1 | products:mpino_16a8r8t [2026/09/15 17:45] (현재) – 바깥 편집 127.0.0.1 | ||
|---|---|---|---|
| 줄 5: | 줄 5: | ||
| Arduino IDE(Arduino C/C++) 또는 MPINO STUDIO 2(Arduino C/C++ & 래더로직)로 프로그램할 수 있습니다. | Arduino IDE(Arduino C/C++) 또는 MPINO STUDIO 2(Arduino C/C++ & 래더로직)로 프로그램할 수 있습니다. | ||
| - | {{ :products:mpino-16a8r8t-connectors.svg?nolink |}} | + | [[:products:mpino_series# |
| - | ===== 주요 | + | |
| + | |||
| + | |||
| + | {{ : | ||
| + | |||
| + | ===== 제품 | ||
| ^ 구분 | ^ 구분 | ||
| 줄 14: | 줄 19: | ||
| | 릴레이 출력 | | 릴레이 출력 | ||
| | 트랜지스터 출력 | | 트랜지스터 출력 | ||
| - | | 아날로그 입력 | + | | 아날로그 입력 |
| | 아날로그 출력 | | 아날로그 출력 | ||
| | 온도센서 입력 | | 온도센서 입력 | ||
| 줄 27: | 줄 32: | ||
| | ::: | ::: | SRAM | 8KB | | | ::: | ::: | SRAM | 8KB | | ||
| | ::: | ::: | EEPROM | | ::: | ::: | EEPROM | ||
| + | |||
| + | **Markdown 자료:** [[https:// | ||
| + | |||
| ===== 디지털 입력 ===== | ===== 디지털 입력 ===== | ||
| - | ^ 그룹 ^ 단자 ^ Arduino | + | [[: |
| - | | COM0 | I(22)~I(29) | D22~D29 | 디지털 입력 8점, COM0 공유 | | + | ^ 그룹 ^ 단자 ^ Arduino 핀 ^ 입력 구성 |
| - | | COM1 | I(30)~I(37) | D30~D37 | 디지털 입력 8점, COM1 공유 | | + | | COM0 | I(22)~I(29) | D22~D29 | 디지털 입력 8점, COM0 공유 |
| + | | COM1 | I(30)~I(37) | D30~D37 | 디지털 입력 8점, COM1 공유 | ||
| 디지털 입력은 양방향 포토커플러로 절연되어 있으며 DC 5~24V의 NPN 또는 PNP 입력을 연결할 수 있습니다. 입력 방식은 COM0·COM1 결선 방향으로 선택합니다. | 디지털 입력은 양방향 포토커플러로 절연되어 있으며 DC 5~24V의 NPN 또는 PNP 입력을 연결할 수 있습니다. 입력 방식은 COM0·COM1 결선 방향으로 선택합니다. | ||
| + | |||
| + | ==== 예제 프로그램 · 디지털 입력 ==== | ||
| + | |||
| + | digitalRead()로 D22~D37의 입력 상태를 200ms마다 표시합니다. HIGH는 1, LOW는 0입니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | for (uint8_t pin = 22; pin <= 37; pin++) { | ||
| + | Serial.print(F(" | ||
| + | Serial.print(pin); | ||
| + | Serial.print(F(": | ||
| + | Serial.println(digitalRead(pin)); | ||
| + | } | ||
| + | delay(200); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · 연속 판독 필터: IdigitalRead() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | bool IdigitalRead(uint8_t pin, uint8_t samples); | ||
| + | </ | ||
| + | |||
| + | IdigitalRead(22, | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const bool inputOn = IdigitalRead(22, | ||
| + | Serial.println(inputOn ? F("D22 ON") : F("D22 OFF" | ||
| + | delay(200); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 채터링 방지: Ibounce(), IbounceOn(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | bool Ibounce(uint8_t pin, uint32_t debounceTime); | ||
| + | bool IbounceOn(uint8_t pin, uint32_t debounceTime); | ||
| + | bool IbounceOff(uint8_t pin, uint32_t debounceTime); | ||
| + | </ | ||
| + | |||
| + | D22 입력을 세 가지 필터로 비교합니다. 반환값은 필터 처리된 ON/OFF 상태입니다. | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | 각 함수는 같은 핀에서도 독립적으로 동작합니다. 필터를 loop()마다 실행하고 결과만 200ms 간격으로 표시합니다. | ||
| + | |||
| + | <code cpp> | ||
| + | unsigned long lastPrint = 0; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const bool stable = Ibounce(22, 30); // D22의 ON/OFF 채터링을 30ms로 필터링 | ||
| + | const bool onDelayed = IbounceOn(22, | ||
| + | const bool offDelayed = IbounceOff(22, | ||
| + | |||
| + | if (millis() - lastPrint >= 200) { | ||
| + | lastPrint = millis(); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(stable); | ||
| + | Serial.print(F(", | ||
| + | Serial.print(onDelayed); | ||
| + | Serial.print(F(", | ||
| + | Serial.println(offDelayed); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 입력 토글: Ialt() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void Ialt(bool input, bool & | ||
| + | </ | ||
| + | |||
| + | Ialt(input, state)는 입력이 OFF에서 ON으로 바뀔 때마다 state를 반전합니다. D22을 한 번 누르면 D39가 ON, 다시 누르면 OFF가 되며 누르고 있는 동안은 유지됩니다. 예제는 Ibounce(22, 30)로 채터링을 걸러 Ialt()에 전달합니다. outputOn은 호출 사이에 유지되는 전역 변수이며, | ||
| + | |||
| + | <code cpp> | ||
| + | bool outputOn = false; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | digitalWrite(39, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const bool previous = outputOn; | ||
| + | const bool inputOn = Ibounce(22, 30); // D22의 ON/OFF 채터링을 30ms로 필터링 | ||
| + | Ialt(inputOn, | ||
| + | digitalWrite(39, | ||
| + | |||
| + | if (outputOn != previous) { | ||
| + | Serial.println(outputOn ? F("D39 ON") : F("D39 OFF" | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 트랜지스터 출력 ===== | ===== 트랜지스터 출력 ===== | ||
| - | ^ 단자 ^ Arduino | + | [[: |
| - | | O(39)~O(46) | D39~D46 | SINK \\ ON: GND 연결 \\ OFF: 개방 | DC 0~55V \\ 최대 3A/ | + | ^ 단자 ^ Arduino 핀 ^ 출력 방식 ^ 정격 |
| + | | O(39)~O(46) | D39~D46 | SINK \\ ON: GND 연결 \\ OFF: 개방 | DC 0~55V \\ 최대 3A/ | ||
| 트랜지스터 출력 부하는 V+와 출력 단자 사이에 연결합니다. | 트랜지스터 출력 부하는 V+와 출력 단자 사이에 연결합니다. | ||
| + | |||
| + | ==== 예제 프로그램 · 트랜지스터 출력 ==== | ||
| + | |||
| + | D22~D29 입력을 D39~D46 출력에 순서대로 대응시킵니다. 입력이 HIGH이면 해당 출력이 ON됩니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | for (uint8_t pin = 39; pin <= 46; pin++) digitalWrite(pin, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | for (uint8_t ch = 0; ch < 8; ch++) { | ||
| + | digitalWrite(39 + ch, digitalRead(22 + ch)); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 릴레이 출력 ===== | ===== 릴레이 출력 ===== | ||
| - | ^ 그룹 ^ 단자 ^ Arduino | + | [[: |
| - | | COM3 | R(62), R(63) | D62, D63 | AC 250V 또는 DC 30V, 최대 5A/1점 | | + | ^ 그룹 ^ 단자 ^ Arduino 핀 ^ 접점 정격 |
| - | | COM4 | R(64), R(65) | D64, D65 | AC 250V 또는 DC 30V, 최대 5A/1점 | | + | | COM3 | R(62), R(63) | D62, D63 | AC 250V 또는 DC 30V, 최대 5A/ |
| - | | COM5 | R(66), R(67) | D66, D67 | AC 250V 또는 DC 30V, 최대 5A/1점 | | + | | COM4 | R(64), R(65) | D64, D65 | AC 250V 또는 DC 30V, 최대 5A/ |
| - | | COM6 | R(68), R(69) | D68, D69 | AC 250V 또는 DC 30V, 최대 5A/1점 | | + | | COM5 | R(66), R(67) | D66, D67 | AC 250V 또는 DC 30V, 최대 5A/ |
| + | | COM6 | R(68), R(69) | D68, D69 | AC 250V 또는 DC 30V, 최대 5A/ | ||
| 릴레이 출력은 각 COM 단자와 출력 단자를 물리적으로 연결하는 무전압 접점입니다. 부하 전원은 별도로 연결합니다. | 릴레이 출력은 각 COM 단자와 출력 단자를 물리적으로 연결하는 무전압 접점입니다. 부하 전원은 별도로 연결합니다. | ||
| + | |||
| + | ==== 예제 프로그램 · 릴레이 출력 ==== | ||
| + | |||
| + | D22~D29 입력을 D62~D69 출력에 순서대로 대응시킵니다. 입력이 HIGH이면 해당 출력이 ON됩니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | for (uint8_t pin = 62; pin <= 69; pin++) digitalWrite(pin, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | for (uint8_t ch = 0; ch < 8; ch++) { | ||
| + | digitalWrite(62 + ch, digitalRead(22 + ch)); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 아날로그 입력ㆍ온도센서 입력 ===== | ===== 아날로그 입력ㆍ온도센서 입력 ===== | ||
| - | ^ 기능 ^ 단자·Arduino | + | ^ 기능 ^ 단자·Arduino 핀 ^ 입력 범위 ^ 분해능 |
| - | | 아날로그 입력 | A(0)~A(3) \\ D54~D57 | DC 0~20mA(기본) \\ DC 0~5V(점퍼 해제) \\ DC 0~10V(선택 사양) | 10bit (0~1023) | | + | | 아날로그 입력 | A(0)~A(3) \\ D54~D57 | DC 0~20mA(기본) \\ DC 0~5V(점퍼 해제) \\ DC 0~10V(선택 사양) | 10bit (0~1023) |
| - | | 온도센서 입력 | T(4), T(5) \\ D58, D59 | NTC 10kΩ(25℃), | + | | 온도센서 입력 | T(4), T(5) \\ D58, D59 | NTC 10kΩ(25℃), |
| + | |||
| + | ==== 예제 프로그램 · 아날로그 입력 ==== | ||
| + | |||
| + | analogRead()로 A0~A3의 ADC 원시값(0~1023)을 표시합니다. 각 채널의 전압·전류 모드는 실제 제품 설정을 따릅니다. | ||
| + | |||
| + | <code cpp> | ||
| + | const uint8_t analogPins[] = {A0, A1, A2, A3}; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | for (uint8_t ch = 0; ch < 4; ch++) { | ||
| + | Serial.print(F(" | ||
| + | Serial.print(ch); | ||
| + | Serial.print(F(" | ||
| + | Serial.println(analogRead(analogPins[ch])); | ||
| + | } | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · 입력 범위 환산: IanalogRead(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | int32_t IanalogRead(uint8_t ch, int32_t min, int32_t max); | ||
| + | float IanalogReadf(uint8_t ch, float min, float max); | ||
| + | </ | ||
| + | |||
| + | A0를 0~5V 입력으로, | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const int32_t percent = IanalogRead(A0, | ||
| + | const float voltage = IanalogReadf(A0, | ||
| + | const float current = IanalogReadf(A2, | ||
| + | Serial.print(F(" | ||
| + | Serial.print(percent); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(voltage, | ||
| + | Serial.print(F(" | ||
| + | Serial.print(current, | ||
| + | Serial.println(F(" | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 4~20mA 범위 환산: IanalogRead2(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | int32_t IanalogRead2(uint8_t ch, int32_t min, int32_t max); | ||
| + | float IanalogRead2f(uint8_t ch, float min, float max); | ||
| + | </ | ||
| + | |||
| + | A0를 0~5V 입력으로, | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const int32_t percent = IanalogRead2(A2, | ||
| + | const float current = IanalogRead2f(A2, | ||
| + | if (percent == ANALOG_READ2_ERROR || | ||
| + | current == (float)ANALOG_READ2_ERROR) { | ||
| + | Serial.println(F(" | ||
| + | } else { | ||
| + | Serial.print(F(" | ||
| + | Serial.print(percent); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(current, | ||
| + | Serial.println(F(" | ||
| + | } | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 이동 평균: analogReadAvg(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | int32_t analogReadAvg(uint8_t ch, uint8_t samples); | ||
| + | int32_t analogRead2Avg(uint8_t ch, uint8_t samples); | ||
| + | </ | ||
| + | |||
| + | A0를 0~5V 입력으로, | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const int32_t voltageAvg = analogReadAvg(A0, | ||
| + | const int32_t currentAvg = analogRead2Avg(A2, | ||
| + | Serial.print(F(" | ||
| + | Serial.print(voltageAvg); | ||
| + | Serial.print(F(", | ||
| + | if (currentAvg == ANALOG_READ2_AVG_ERROR) { | ||
| + | Serial.println(F(" | ||
| + | } else { | ||
| + | Serial.println(currentAvg); | ||
| + | } | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 예제 프로그램 · 온도센서 입력 ==== | ||
| + | |||
| + | analogRead()로 A4, A5의 온도센서 ADC 원시값(0~1023)을 표시합니다. 섭씨 환산은 아래 내장 명령어에서 설명합니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | Serial.print(F(" | ||
| + | Serial.println(analogRead(A4)); | ||
| + | Serial.print(F(" | ||
| + | Serial.println(analogRead(A5)); | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · NTC 온도 읽기: ntcRead(), ntcReadf() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | int ntcRead(uint8_t ch); | ||
| + | float ntcReadf(uint8_t ch); | ||
| + | </ | ||
| + | |||
| + | NTC 입력 A4에 연결한 NTC 10kΩ(25℃), | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | const int temperature10 = ntcRead(A4); | ||
| + | const float temperature = ntcReadf(A4); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(temperature10); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(temperature, | ||
| + | Serial.println(F(" | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| - | 아날로그 입력과 온도센서 입력을 사용할 때는 '' | ||
| ===== 아날로그 출력 ===== | ===== 아날로그 출력 ===== | ||
| - | ^ 단자 ^ Arduino | + | ^ 단자 ^ Arduino 핀 ^ 출력 범위 ^ 분해능 |
| - | | AO(6), AO(7) | D6, D7 | DC 0~5V | 16bit (0~65535) | | + | | AO(6), AO(7) | D6, D7 | DC 0~5V | 16bit (0~65535) |
| 아날로그 출력은 TIMER4를 사용합니다. TCNT4 고속카운터 입력도 TIMER4를 사용하므로 두 기능은 동시에 사용할 수 없습니다. | 아날로그 출력은 TIMER4를 사용합니다. TCNT4 고속카운터 입력도 TIMER4를 사용하므로 두 기능은 동시에 사용할 수 없습니다. | ||
| + | |||
| + | ==== 예제 프로그램 · 아날로그 출력 ==== | ||
| + | |||
| + | analogWrite()의 기본 8비트 값 128로 AO(6)·AO(7)에 약 2.5V를 출력합니다. 16비트 전압 환산은 아래 내장 명령어를 사용합니다. 외부 12~24V 전원이 필요하며 Timer4 고속카운터와 동시에 사용하지 않습니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | analogWrite(6, | ||
| + | analogWrite(7, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · 출력 범위 환산: IanalogWrite(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void analogWriteInit(uint8_t ch, uint16_t top = 65535, uint16_t prescaler = 1); | ||
| + | void IanalogWrite(uint8_t ch, int32_t min, int32_t max, int32_t value); | ||
| + | void IanalogWritef(uint8_t ch, float min, float max, float value); | ||
| + | </ | ||
| + | |||
| + | AO(6)에는 0~5000mV 범위의 2500을, AO(7)에는 0~5V 범위의 2.5를 지정해 약 2.5V를 출력합니다. analogWriteInit(6)으로 두 채널이 공유하는 Timer4를 16비트 TOP=65535로 먼저 설정합니다. 두 채널은 Timer4를 공유하며 TCNT4와 동시에 사용하지 않습니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | analogWriteInit(6); | ||
| + | IanalogWrite(6, | ||
| + | IanalogWritef(7, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 고속카운터ㆍ인터럽트ㆍ엔코더 ===== | ===== 고속카운터ㆍ인터럽트ㆍ엔코더 ===== | ||
| - | ^ 기능 ^ 단자·Arduino | + | ^ 기능 ^ 단자·Arduino 핀 ^ 채널 ^ |
| | 절연 고속카운터 | TCNT4(D74), TCNT5(D47), COM2 | 2점, 최대 5kHz | | | 절연 고속카운터 | TCNT4(D74), TCNT5(D47), COM2 | 2점, 최대 5kHz | | ||
| | 외부 인터럽트 | P(2)(D2), SCL(D21), SDA(D20) | 3점 | | | 외부 인터럽트 | P(2)(D2), SCL(D21), SDA(D20) | 3점 | | ||
| 줄 76: | 줄 445: | ||
| SDA와 SCL을 엔코더 입력이나 외부 인터럽트로 사용하면 I²C 통신을 동시에 사용할 수 없습니다. P(2)는 펄스 출력과 외부 인터럽트 기능을 공유합니다. | SDA와 SCL을 엔코더 입력이나 외부 인터럽트로 사용하면 I²C 통신을 동시에 사용할 수 없습니다. P(2)는 펄스 출력과 외부 인터럽트 기능을 공유합니다. | ||
| + | |||
| + | ==== 예제 프로그램 · 펄스 카운트 ==== | ||
| + | |||
| + | TCNT4·TCNT5 절연 입력을 타이머 외부 클럭으로 계수합니다. MCU 타이머 입력의 상승 에지를 사용하며, | ||
| + | |||
| + | <code cpp> | ||
| + | #include < | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | TIMSK4 = 0; | ||
| + | TCCR4A = 0; | ||
| + | TCCR4B = 0x07; | ||
| + | TCNT4 = 0; | ||
| + | TIMSK5 = 0; | ||
| + | TCCR5A = 0; | ||
| + | TCCR5B = 0x07; | ||
| + | TCNT5 = 0; | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | uint16_t first, second; | ||
| + | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { | ||
| + | first = TCNT4; | ||
| + | second = TCNT5; | ||
| + | } | ||
| + | Serial.print(F(" | ||
| + | Serial.print(first); | ||
| + | Serial.print(F(", | ||
| + | Serial.println(second); | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | SDA(D20)의 FALLING 에지를 계수합니다. 입력은 4.7kΩ 풀업 (SDA/ | ||
| + | |||
| + | <code cpp> | ||
| + | static_assert(digitalPinToInterrupt(SDA) != NOT_AN_INTERRUPT, | ||
| + | |||
| + | #include < | ||
| + | |||
| + | volatile uint32_t pulseCount = 0; | ||
| + | |||
| + | void countPulse() { | ||
| + | pulseCount++; | ||
| + | } | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | // SDA는 풀업 상태이며, | ||
| + | attachInterrupt(digitalPinToInterrupt(SDA), | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | uint32_t snapshot; | ||
| + | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { | ||
| + | snapshot = pulseCount; | ||
| + | } | ||
| + | Serial.print(F(" | ||
| + | Serial.println(snapshot); | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · 하드웨어 펄스 카운터 설정: TCNTSETUP() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void TCNTSETUP(uint8_t timerNumber, | ||
| + | </ | ||
| + | |||
| + | TCNTSETUP(timerNumber, | ||
| + | |||
| + | <code cpp> | ||
| + | #include < | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | TCNTSETUP(4, | ||
| + | TCNTSETUP(5, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | uint16_t first, second; | ||
| + | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { | ||
| + | first = TCNT4; | ||
| + | second = TCNT5; | ||
| + | } | ||
| + | Serial.print(F(" | ||
| + | Serial.print(first); | ||
| + | Serial.print(F(", | ||
| + | Serial.println(second); | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 예제 프로그램 · 엔코더 입력 ==== | ||
| + | |||
| + | A상은 SDA(D20), B상은 SCL(D21)에 연결합니다. 4.7kΩ 풀업 (SDA/ | ||
| + | |||
| + | <code cpp> | ||
| + | static_assert(digitalPinToInterrupt(SDA) != NOT_AN_INTERRUPT, | ||
| + | |||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | volatile int32_t position = 0; | ||
| + | |||
| + | void readEncoder() { | ||
| + | // A상 하강 시 B상이 LOW이면 증가 (1배 카운트) | ||
| + | if (digitalRead(SCL) == LOW) { | ||
| + | if (position < INT32_MAX) position++; | ||
| + | } else { | ||
| + | if (position > INT32_MIN) position--; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | // 엔코더 A상 → SDA(D20), B상 → SCL(D21) | ||
| + | attachInterrupt(digitalPinToInterrupt(SDA), | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | int32_t snapshot; | ||
| + | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { | ||
| + | snapshot = position; | ||
| + | } | ||
| + | Serial.print(F(" | ||
| + | Serial.println(snapshot); | ||
| + | delay(500); | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 펄스 출력 ===== | ===== 펄스 출력 ===== | ||
| - | ^ 단자 ^ Arduino | + | ^ 단자 ^ Arduino 핀 ^ 타이머 |
| - | | P(11), P(12) | D11, D12 | TIMER1 | | + | | P(11), P(12) | D11, D12 | TIMER1 |
| - | | P(5), P(2) | D5, D2 | TIMER3 | | + | | P(5), P(2) | D5, D2 | TIMER3 |
| 펄스 출력은 기본 8bit 듀티를 사용하며 출력 전압은 DC 0~5V입니다. 타이머 레지스터를 설정하면 16bit 듀티를 사용할 수 있습니다. | 펄스 출력은 기본 8bit 듀티를 사용하며 출력 전압은 DC 0~5V입니다. 타이머 레지스터를 설정하면 16bit 듀티를 사용할 수 있습니다. | ||
| + | |||
| + | ==== 예제 프로그램 · PWM 출력 ==== | ||
| + | |||
| + | analogWrite()의 0~255 듀티값 중 128을 D11, D12, D5, D2에 지정해 약 50% PWM을 출력합니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | analogWrite(11, | ||
| + | analogWrite(12, | ||
| + | analogWrite(5, | ||
| + | analogWrite(2, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · 듀티값 지정: PWM() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void PWM(uint8_t pin, uint16_t val, bool onDutybit16 = false); | ||
| + | </ | ||
| + | |||
| + | PWM(pin, val, onDutybit16)은 듀티값을 지정합니다. 8비트는 0~255, 세 번째 인수가 true인 16비트는 0~65535입니다. D11에 16비트 중간값 32768을 출력합니다. D11·D12는 Timer1, D5·D2는 Timer3를 공유하므로 같은 타이머는 같은 분해능으로 사용합니다. PWM_RESET() 후 시작합니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | PWM_RESET(); | ||
| + | PWM(11, 32768, true); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 주파수·듀티 설정: FDPWM() === | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void FDPWM(uint8_t pin, int32_t intHz, float Duty); | ||
| + | </ | ||
| + | |||
| + | FDPWM(pin, intHz, Duty)는 주파수(Hz)와 듀티(%)를 지정합니다. D11에 1kHz, 50%를 출력합니다. D11·D12는 Timer1, D5·D2는 Timer3를 공유하므로 같은 타이머의 출력은 같은 주파수를 지정합니다. 별도 FPWM() 함수는 없으며 50% 출력은 FDPWM(pin, hz, 50.0f)로 설정합니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | PWM_RESET(); | ||
| + | FDPWM(11, 1000, 50.0f); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 지정 개수 펄스 출력: NPWM_BEGIN(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void NPWM_BEGIN(uint8_t pin, uint32_t intHz, float Duty, uint32_t N); | ||
| + | void NPWM(uint8_t pin); | ||
| + | </ | ||
| + | |||
| + | NPWM_BEGIN()으로 D11의 1kHz·50% 펄스 100개를 준비하고 NPWM()을 loop()마다 실행해 출력 및 완료 처리를 합니다. delay()를 넣지 않습니다. D11·D12는 Timer1, D5·D2는 Timer3를 공유하며, | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | PWM_RESET(); | ||
| + | NPWM_BEGIN(11, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | NPWM(11); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · 채널 출력 정지·재개: | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void PWMOFF(uint8_t pin, bool POff); | ||
| + | </ | ||
| + | |||
| + | PWMOFF(pin, true)는 채널 정지 플래그를 설정하고 false는 해제합니다. 설정 직후 FDPWM()을 호출해 출력 연결에 반영합니다. D11을 2초 켜고 2초 끄며, 정지 후 digitalWrite()로 LOW를 유지합니다. NPWM()에는 이 정지 플래그가 적용되지 않습니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | PWM_RESET(); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | PWMOFF(11, false); | ||
| + | FDPWM(11, 1000, 50.0f); | ||
| + | delay(2000); | ||
| + | |||
| + | PWMOFF(11, true); | ||
| + | FDPWM(11, 1000, 50.0f); | ||
| + | digitalWrite(11, | ||
| + | delay(2000); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · PWM 타이머 정지·초기화: | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void PWM_RESET(); | ||
| + | </ | ||
| + | |||
| + | PWM_RESET()은 Timer1·Timer3·Timer4·Timer5 제어 레지스터와 카운터를 초기화합니다. D11에 1kHz를 2초 출력한 후 전체 타이머를 정지합니다. 같은 타이머의 PWM·카운터·아날로그 출력에도 영향을 줍니다. PWMOFF()의 정지 플래그까지 해제하지는 않습니다. | ||
| + | |||
| + | <code cpp> | ||
| + | void setup() { | ||
| + | PWM_RESET(); | ||
| + | FDPWM(11, 1000, 50.0f); | ||
| + | delay(2000); | ||
| + | PWM_RESET(); | ||
| + | digitalWrite(11, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | } | ||
| + | </ | ||
| + | |||
| ===== 통신과 다운로드 ===== | ===== 통신과 다운로드 ===== | ||
| - | ^ 기능 ^ Arduino | + | ^ 기능 ^ Arduino 핀 ^ 사용 객체 |
| - | | USB 다운로드·모니터링 | D0(RX0), D1(TX0) | '' | + | | USB 다운로드·모니터링 | D0(RX0), D1(TX0) | '' |
| - | | RS-232 | D19(RX1), D18(TX1) | '' | + | | RS-232 | D19(RX1), D18(TX1) | '' |
| - | | RS-485 | D17(RX2), D16(TX2) | '' | + | | RS-485 | D17(RX2), D16(TX2) | '' |
| - | | UART | D15(RX3), D14(TX3) | '' | + | | UART | D15(RX3), D14(TX3) | '' |
| - | | I²C | D20(SDA), D21(SCL) | '' | + | | I²C | D20(SDA), D21(SCL) | '' |
| RS-485는 자동 방향제어 방식이므로 송수신 방향을 전환하는 별도의 제어 핀이 필요하지 않습니다. | RS-485는 자동 방향제어 방식이므로 송수신 방향을 전환하는 별도의 제어 핀이 필요하지 않습니다. | ||
| - | ===== 기본 입출력 | + | ==== 예제 |
| - | 아래 예제는 I(22) 입력이 ON되면 R(62) 릴레이를 | + | Serial(115200bps)과 RS-232 Serial1(9600bps, 8-N-1) 사이에 문자를 전달합니다. availableForWrite()로 송신 버퍼 여유를 확인합니다. Serial2는 RS-485, Serial3는 TTL UART이며 해당 단자 사용 |
| <code cpp> | <code cpp> | ||
| + | // MP 다운로드 케이블의 Serial 모니터 ↔ RS-232 Serial1 | ||
| void setup() { | void setup() { | ||
| - | | + | |
| - | | + | |
| } | } | ||
| void loop() { | void loop() { | ||
| - | | + | |
| + | Serial1.write(Serial.read()); | ||
| + | } | ||
| + | if (Serial1.available() && Serial.availableForWrite()) { | ||
| + | Serial.write(Serial1.read()); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 예제 프로그램 · I²C 통신 ==== | ||
| + | |||
| + | Wire로 주소 0x08~0x77을 검색하고 응답한 주소를 3초마다 표시합니다. SDA·SCL을 사용하는 엔코더/ | ||
| + | |||
| + | <code cpp> | ||
| + | #include < | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | Wire.begin(); | ||
| + | Wire.setClock(100000); | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | uint8_t found = 0; | ||
| + | for (uint8_t address = 0x08; address <= 0x77; address++) { | ||
| + | Wire.beginTransmission(address); | ||
| + | const uint8_t result = Wire.endTransmission(); | ||
| + | if (result == 0) { | ||
| + | Serial.print(F(" | ||
| + | if (address < 16) Serial.print(' | ||
| + | Serial.println(address, HEX); | ||
| + | found++; | ||
| + | } | ||
| + | } | ||
| + | Serial.print(F(" | ||
| + | Serial.println(found); | ||
| + | delay(3000); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 예제 프로그램 · Modbus RTU 통신 ==== | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · Modbus RTU Slave: ImodbusRTUMem(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void ImodbusRTUMem(uint16_t m_size, uint16_t d_size); | ||
| + | void ImodbusRTUAdr(uint16_t m_offset, uint16_t d_offset); | ||
| + | void ImodbusRTUInit(HardwareSerial& | ||
| + | void ImodbusRTU(); | ||
| + | </ | ||
| + | |||
| + | D22 상태를 M[0], A0 원시값(0~1023)을 D[0]에 넣어 상대 기기에서 읽습니다. RS-232(Serial1), | ||
| + | |||
| + | M과 D는 코어가 제공하는 공유 전역 메모리입니다. 이 스케치는 단독 실행하며, | ||
| + | |||
| + | <code cpp> | ||
| + | unsigned long lastPrint = 0; | ||
| + | bool memoryReady = false; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | ImodbusRTUMem(8, | ||
| + | ImodbusRTUAdr(0, | ||
| + | ImodbusRTUInit(Serial1, | ||
| + | memoryReady = (M != nullptr && D != nullptr); | ||
| + | if (!memoryReady) Serial.println(F(" | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | if (!memoryReady) return; | ||
| + | M[0] = digitalRead(22); | ||
| + | D[0] = analogRead(A0); | ||
| + | ImodbusRTU(); | ||
| + | |||
| + | if (millis() - lastPrint >= 1000) { | ||
| + | lastPrint = millis(); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(M[0]); | ||
| + | Serial.print(F(", | ||
| + | Serial.println(D[0]); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === 함수 예제 · Modbus RTU Master: ImodbusRTUmasterInit(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void ImodbusRTUmasterInit(HardwareSerial& | ||
| + | uint8_t ImodbusRTUmaster(HardwareSerial& | ||
| + | </ | ||
| + | |||
| + | RS-232(Serial1)로 상대 슬레이브의 홀딩 레지스터 1개를 1초마다 읽습니다. 상대 기기는 국번 1, 9600bps, 8N1, FC03·실제 시작 주소 0으로 준비합니다. 응답은 received[0]에 저장하며 성공한 경우에만 표시합니다. 오류 코드는 16진수로 표시합니다. 위 슬레이브 예제와는 별도 스케치이며, | ||
| + | |||
| + | <code cpp> | ||
| + | unsigned long lastRequest = 0; | ||
| + | uint16_t received[1]; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | ImodbusRTUmasterInit(Serial1, | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | if (millis() - lastRequest < 1000) return; | ||
| + | lastRequest = millis(); | ||
| + | const uint8_t result = ImodbusRTUmaster(Serial1, | ||
| + | |||
| + | if (result == IMODBUS_RTU_SUCCESS) { | ||
| + | Serial.print(F(" | ||
| + | Serial.println(received[0]); | ||
| + | } else { | ||
| + | Serial.print(F(" | ||
| + | Serial.println(result, | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 예제 프로그램 · LS산전 Cnet 통신 ==== | ||
| + | |||
| + | **관련 내장 명령어** | ||
| + | |||
| + | === 함수 예제 · LS산전 Cnet SLave: ICnetMem(), ICnetAdr(), ICnetInit(), | ||
| + | |||
| + | **함수 원형** | ||
| + | |||
| + | <code cpp> | ||
| + | void ICnetMem(uint16_t m_size, uint16_t d_size, uint16_t r_size = 100); | ||
| + | void ICnetAdr(uint16_t m_offset, uint16_t d_offset, uint16_t r_offset = 0); | ||
| + | void ICnetInit(HardwareSerial& | ||
| + | void ICnet(); | ||
| + | </ | ||
| + | |||
| + | D22 상태를 M[0], A0 원시값(0~1023)을 D[0]에 넣어 HMI에서 읽습니다. 상대 기기는 LS산전 Cnet, RS-232, 국번 1, 9600bps, 8N1로 설정합니다. 비트는 %MX00000(M[0]), | ||
| + | |||
| + | M·D·R은 코어가 제공하는 공유 전역 메모리이며, | ||
| + | |||
| + | <code cpp> | ||
| + | unsigned long lastPrint = 0; | ||
| + | bool memoryReady = false; | ||
| + | |||
| + | void setup() { | ||
| + | Serial.begin(115200); | ||
| + | ICnetMem(8, 8, 0); // M 8비트와 D 8워드 할당, R은 사용하지 않음 | ||
| + | ICnetAdr(0, 0, 0); // M과 D의 통신 시작 주소를 0으로 설정 | ||
| + | ICnetInit(Serial1, | ||
| + | memoryReady = (M != nullptr && D != nullptr); | ||
| + | if (!memoryReady) Serial.println(F(" | ||
| + | } | ||
| + | |||
| + | void loop() { | ||
| + | if (!memoryReady) return; | ||
| + | M[0] = digitalRead(22); | ||
| + | D[0] = analogRead(A0); | ||
| + | ICnet(); | ||
| + | |||
| + | if (millis() - lastPrint >= 1000) { | ||
| + | lastPrint = millis(); | ||
| + | Serial.print(F(" | ||
| + | Serial.print(M[0]); | ||
| + | Serial.print(F(", | ||
| + | Serial.println(D[0]); | ||
| + | } | ||
| } | } | ||
| </ | </ | ||
products/mpino_16a8r8t.1788412908.txt.gz · 마지막으로 수정됨: 저자 127.0.0.1
