diff --git a/examples/pin_test/pin_test.ino b/examples/pin_test/pin_test.ino new file mode 100644 index 0000000..d625842 --- /dev/null +++ b/examples/pin_test/pin_test.ino @@ -0,0 +1,342 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +/* + * M5IOE1 引脚测试示例 + * M5IOE1 Pin Test Example + * + * 本示例展示 M5IOE1 库的核心功能 + * This example demonstrates the core features of M5IOE1 library + * + * 硬件连接 / Hardware Connections: + * - M5IOE1 SDA -> GPIO 38, SCL -> GPIO 39 + * - IO1 -> 按钮 (Button, active LOW to GND) + * - IO3 -> LED (through 220Ω to GND) + * - IO2 -> 电位器 / Potentiometer (ADC Channel 1, 0-3.3V) + * - IO9 -> LED (PWM Channel 0, through 220Ω to GND) + */ + +#include + +// ============================ +// 配置 / Configuration +// ============================ +#define I2C_SDA_PIN 38 +#define I2C_SCL_PIN 39 +#define I2C_FREQ 400000 +#define I2C_ADDR M5IOE1_DEFAULT_ADDR + +// ============================ +// 引脚定义 / Pin Definitions +// ============================ +// IO1 (Pin 0) - 数字输入,按钮 / Digital input, button +#define PIN_BUTTON M5IOE1_PIN_1 // IO1 +// IO3 (Pin 2) - 数字输出,LED / Digital output, LED +#define PIN_LED M5IOE1_PIN_3 // IO3 +// IO2 (Pin 1) - ADC 输入,电位器 / ADC input, potentiometer (ADC Channel 1) +#define PIN_ADC M5IOE1_PIN_2 // IO2 -> ADC_CH1 +// IO9 (Pin 8) - PWM 输出,LED / PWM output, LED (PWM Channel 0) +#define PIN_PWM M5IOE1_PIN_9 // IO9 -> PWM_CH1 + +// ============================ +// 全局变量 / Global Variables +// ============================ +M5IOE1 ioe1; + +volatile int buttonPressCount = 0; +uint8_t pwmValue = 0; +int pwmDirection = 1; + +// ============================ +// 中断回调函数 / Interrupt Callback +// ============================ +void IRAM_ATTR buttonPressed() { + buttonPressCount++; +} + +// ============================ +// 辅助函数 / Helper Functions +// ============================ +void printSeparator(const char* title) { + Serial.println("\n========================================"); + Serial.println(title); + Serial.println("========================================\n"); +} + +void testDigitalWrite() { + printSeparator("测试 digitalWrite() / Testing digitalWrite()"); + + Serial.println("Blinking LED on IO3 (3 cycles)...\n"); + + for (int i = 0; i < 3; i++) { + Serial.print("Cycle "); + Serial.print(i + 1); + Serial.println(": HIGH -> LOW"); + ioe1.digitalWrite(PIN_LED, HIGH); + delay(500); + ioe1.digitalWrite(PIN_LED, LOW); + delay(500); + } + + Serial.println("digitalWrite() test completed!\n"); +} + +void testDigitalRead() { + printSeparator("测试 digitalRead() / Testing digitalRead()"); + + Serial.println("Reading button on IO1 (active LOW) for 8 seconds..."); + Serial.println("Press the button to test!\n"); + + unsigned long startTime = millis(); + int lastState = -1; + + while (millis() - startTime < 8000) { + int currentState = ioe1.digitalRead(PIN_BUTTON); + + if (currentState != lastState) { + Serial.print("State: "); + Serial.println(currentState == LOW ? "LOW (pressed)" : "HIGH (released)"); + lastState = currentState; + } + delay(50); + } + + Serial.println("\ndigitalRead() test completed!\n"); +} + +void testPinMode() { + printSeparator("测试 pinMode() / Testing pinMode()"); + + Serial.println("Testing pin modes on IO3:\n"); + + // OUTPUT + 闪烁测试 / OUTPUT + Blink test + Serial.println("1. OUTPUT mode"); + ioe1.pinMode(PIN_LED, OUTPUT); + for (int i = 0; i < 2; i++) { + ioe1.digitalWrite(PIN_LED, HIGH); + delay(200); + ioe1.digitalWrite(PIN_LED, LOW); + delay(200); + } + Serial.println(" -> Blink test OK\n"); + + // INPUT + 读取测试 / INPUT + Read test + Serial.println("2. INPUT mode (floating)"); + ioe1.pinMode(PIN_LED, INPUT); + int val = ioe1.digitalRead(PIN_LED); + Serial.print(" -> Read: "); + Serial.println(val == HIGH ? "HIGH" : "LOW"); + Serial.println(" -> OK\n"); + + // INPUT_PULLUP + 读取测试 / INPUT_PULLUP + Read test + Serial.println("3. INPUT_PULLUP mode"); + ioe1.pinMode(PIN_LED, INPUT_PULLUP); + val = ioe1.digitalRead(PIN_LED); + Serial.print(" -> Read: "); + Serial.println(val == HIGH ? "HIGH (pulled up)" : "LOW"); + Serial.println(" -> OK\n"); + + // 恢复输出模式 / Restore OUTPUT mode + ioe1.pinMode(PIN_LED, OUTPUT); + + Serial.println("pinMode() test completed!\n"); +} + +void testAnalogRead() { + printSeparator("测试 analogRead() / Testing analogRead()"); + + Serial.println("Reading ADC Channel 1 (IO2) for 8 seconds..."); + Serial.println("Rotate potentiometer to test!\n"); + + unsigned long startTime = millis(); + unsigned long lastPrint = 0; + + while (millis() - startTime < 8000) { + uint16_t adcValue; + + if (ioe1.analogRead(1, &adcValue)) { + if (millis() - lastPrint > 500) { + float voltage = (adcValue * 3300.0f) / 4095.0f; + + Serial.print("Value: "); + Serial.print(adcValue); + Serial.print(" ("); + Serial.print((int)voltage); + Serial.println(" mV)"); + + // 简单的条形图 / Simple bar graph + int bars = map(adcValue, 0, 4095, 0, 20); + Serial.print("["); + for (int i = 0; i < 20; i++) { + Serial.print(i < bars ? "=" : " "); + } + Serial.println("]\n"); + + lastPrint = millis(); + } + } + delay(50); + } + + Serial.println("analogRead() test completed!\n"); +} + +void testAnalogWrite() { + printSeparator("测试 analogWrite() / Testing analogWrite()"); + + Serial.println("PWM breathing on IO9 (Channel 0)...\n"); + + ioe1.setPwmFrequency(5000); + Serial.println("PWM frequency: 5000 Hz\n"); + + unsigned long startTime = millis(); + + while (millis() - startTime < 8000) { + if (ioe1.analogWrite(0, pwmValue)) { + if (pwmValue % 25 == 0) { + float duty = (pwmValue * 100.0f) / 255.0f; + Serial.print("PWM value: "); + Serial.print(pwmValue); + Serial.print(" ("); + Serial.print((int)duty); + Serial.println("%)"); + } + } + + // 呼吸效果 / Breathing effect + pwmValue += pwmDirection * 5; + if (pwmValue >= 255) { + pwmValue = 255; + pwmDirection = -1; + } else if (pwmValue <= 0) { + pwmValue = 0; + pwmDirection = 1; + } + + delay(20); + } + + ioe1.analogWrite(0, 0); + Serial.println("\nPWM turned off.\n"); + + Serial.println("analogWrite() test completed!\n"); +} + +void testAttachInterrupt() { + printSeparator("测试 attachInterrupt() / Testing attachInterrupt()"); + + Serial.println("Configuring button with interrupt (FALLING edge)...\n"); + + ioe1.pinMode(PIN_BUTTON, INPUT_PULLUP); + buttonPressCount = 0; + ioe1.attachInterrupt(PIN_BUTTON, buttonPressed, FALLING); + + Serial.println("Interrupt attached! Press button for 8 seconds...\n"); + + unsigned long startTime = millis(); + int lastCount = 0; + + while (millis() - startTime < 8000) { + if (buttonPressCount != lastCount) { + Serial.print("Button press detected! Count: "); + Serial.println(buttonPressCount); + lastCount = buttonPressCount; + } + delay(50); + } + + Serial.print("\nTotal presses: "); + Serial.println(buttonPressCount); + + ioe1.detachInterrupt(PIN_BUTTON); + Serial.println("Interrupt detached.\n"); + + Serial.println("attachInterrupt() test completed!\n"); +} + +// ============================ +// Arduino setup() / loop() +// ============================ +void setup() { + Serial.begin(115200); + delay(1000); + + Serial.println("\n\n######################################"); + Serial.println("# M5IOE1 Pin Test Example #"); + Serial.println("######################################\n"); + + // 设置日志级别 / Set log level + M5IOE1::setLogLevel(M5IOE1_LOG_LEVEL_WARN); + + // 初始化 M5IOE1 / Initialize M5IOE1 + Serial.println("Initializing M5IOE1..."); + if (!ioe1.begin(&Wire, I2C_ADDR, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQ)) { + Serial.println("ERROR: Failed to initialize M5IOE1!"); + Serial.println("Please check I2C connections and power supply."); + while (1) { delay(1000); } + } + Serial.println("M5IOE1 initialized successfully!\n"); + + // 读取设备信息 / Read device info + uint16_t uid; + uint8_t version; + if (ioe1.getUID(&uid)) { + Serial.println("Device UID: 0x" + String(uid, HEX)); + } + if (ioe1.getVersion(&version)) { + Serial.println("Firmware Version: " + String(version)); + } + Serial.println(); + + // 初始化测试引脚 / Initialize test pins + Serial.println("Initializing test pins..."); + ioe1.pinMode(PIN_BUTTON, INPUT_PULLUP); + ioe1.pinMode(PIN_LED, OUTPUT); + ioe1.digitalWrite(PIN_LED, LOW); + Serial.println("Test pins ready!\n"); + + delay(2000); +} + +void loop() { + static int testStep = 0; + + switch (testStep) { + case 0: + testPinMode(); + break; + case 1: + testDigitalWrite(); + break; + case 2: + testDigitalRead(); + break; + case 3: + testAnalogRead(); + break; + case 4: + testAnalogWrite(); + break; + case 5: + testAttachInterrupt(); + break; + default: + Serial.println("\n\n######################################"); + Serial.println("# All tests completed! #"); + Serial.println("# Restarting in 5 seconds... #"); + Serial.println("######################################\n"); + delay(5000); + testStep = -1; + break; + } + + testStep++; + + if (testStep != 0) { + Serial.println("Next test starting in 2 seconds...\n"); + delay(2000); + } +} diff --git a/src/M5IOE1.cpp b/src/M5IOE1.cpp index cf819c0..a020f3c 100644 --- a/src/M5IOE1.cpp +++ b/src/M5IOE1.cpp @@ -910,28 +910,30 @@ void M5IOE1::pinMode(uint8_t pin, uint8_t mode) { if (!_readReg16(M5IOE1_REG_GPIO_DRV_L, &drvReg)) return; switch (mode) { - case INPUT: + case INPUT: // 0x01 modeReg &= ~(1 << pin); puReg &= ~(1 << pin); pdReg &= ~(1 << pin); _pinStates[pin].isOutput = false; _pinStates[pin].pull = 0; break; - case INPUT_PULLUP: + case PULLUP: // 0x04 - 仅上拉 + case INPUT_PULLUP: // 0x05 modeReg &= ~(1 << pin); puReg |= (1 << pin); pdReg &= ~(1 << pin); _pinStates[pin].isOutput = false; _pinStates[pin].pull = 1; break; - case INPUT_PULLDOWN: + case PULLDOWN: // 0x08 - 仅下拉 + case INPUT_PULLDOWN: // 0x09 modeReg &= ~(1 << pin); puReg &= ~(1 << pin); pdReg |= (1 << pin); _pinStates[pin].isOutput = false; _pinStates[pin].pull = 2; break; - case OUTPUT: + case OUTPUT: // 0x03 // 如果此引脚上启用了 PWM 则禁用 // Disable PWM if enabled on this pin if (_isPwmPin(pin)) { @@ -953,6 +955,38 @@ void M5IOE1::pinMode(uint8_t pin, uint8_t mode) { _pinStates[pin].isOutput = true; _pinStates[pin].drive = 0; break; + case OPEN_DRAIN: // 0x10 + case OUTPUT_OPEN_DRAIN: // 0x13 + // 如果此引脚上启用了 PWM 则禁用 + // Disable PWM if enabled on this pin + if (_isPwmPin(pin)) { + uint8_t ch = _getPwmChannel(pin); + uint8_t regL = M5IOE1_REG_PWM1_DUTY_L + ch * 2; + uint16_t pwmData = 0; + if (_readReg16(regL, &pwmData)) { + if (pwmData & ((uint16_t)M5IOE1_PWM_ENABLE << 8)) { + pwmData &= ~((uint16_t)M5IOE1_PWM_ENABLE << 8); + _writeReg16(regL, pwmData); + } + } + } + modeReg |= (1 << pin); + puReg &= ~(1 << pin); + pdReg &= ~(1 << pin); + drvReg |= (1 << pin); // 开漏 + // Open-drain + _pinStates[pin].isOutput = true; + _pinStates[pin].drive = 1; + break; + case ANALOG: // 0xC0 + // 模拟模式 - 设置为输入,无上拉下拉 + // Analog mode - set as input, no pull-up/down + modeReg &= ~(1 << pin); + puReg &= ~(1 << pin); + pdReg &= ~(1 << pin); + _pinStates[pin].isOutput = false; + _pinStates[pin].pull = 0; + break; default: M5IOE1_LOG_E(TAG, "Invalid mode: %d", mode); return; @@ -1386,6 +1420,34 @@ bool M5IOE1::getPwmDuty12bit(uint8_t channel, uint16_t* duty12, bool* polarity, return true; } +// ============================ +// Arduino 兼容 analogWrite +// Arduino-compatible analogWrite +// ============================ + +bool M5IOE1::analogWrite(uint8_t channel, uint8_t value) { + if (channel > 3 || !_initialized) { + M5IOE1_LOG_E(TAG, "Invalid channel or not initialized: ch=%d", channel); + return false; + } + + // 值为 0 时关闭 PWM 输出 + // Turn off PWM when value is 0 + if (value == 0) { + return setPwmDuty12bit(channel, 0, false, false); + } + + // 将 8-bit 值 (0-255) 缩放到 12-bit (0-4095) + // Scale 8-bit value (0-255) to 12-bit (0-4095) + // 公式:duty12 = (value * 4095) / 255 = value * 16 + value / 16 + // Formula: duty12 = (value * 4095) / 255 = value * 16 + value / 16 + uint16_t duty12 = (uint16_t)value * 16 + (uint16_t)value / 16; + + // 设置 PWM,默认启用输出,正常极性 + // Set PWM with output enabled, normal polarity + return setPwmDuty12bit(channel, duty12, false, true); +} + // ============================ // NeoPixel LED 功能 // NeoPixel LED Functions diff --git a/src/M5IOE1.h b/src/M5IOE1.h index d7eff8d..3cc2eb9 100644 --- a/src/M5IOE1.h +++ b/src/M5IOE1.h @@ -172,43 +172,76 @@ typedef enum { #define M5IOE1_PWM_CH3 2 // IO11 (pin index 10) #define M5IOE1_PWM_CH4 3 // IO10 (pin index 9) -// ============================ -// GPIO 模式定义(Arduino 兼容) -// GPIO Mode Definitions (Arduino-compatible) -// ============================ -#ifndef INPUT -#define INPUT 0x00 -#endif -#ifndef OUTPUT -#define OUTPUT 0x01 -#endif -#ifndef INPUT_PULLUP -#define INPUT_PULLUP 0x02 -#endif -#ifndef INPUT_PULLDOWN -#define INPUT_PULLDOWN 0x03 -#endif - // ============================ // GPIO 电平定义 // GPIO Level Definitions // ============================ #ifndef LOW -#define LOW 0 +#define LOW 0x0 #endif #ifndef HIGH -#define HIGH 1 +#define HIGH 0x1 +#endif + +// ============================ +// GPIO 模式定义(Arduino 兼容) +// GPIO Mode Definitions (Arduino-compatible) +// ============================ +#ifndef INPUT +#define INPUT 0x01 +#endif +#ifndef OUTPUT +#define OUTPUT 0x03 +#endif +#ifndef PULLUP +#define PULLUP 0x04 +#endif +#ifndef INPUT_PULLUP +#define INPUT_PULLUP 0x05 +#endif +#ifndef PULLDOWN +#define PULLDOWN 0x08 +#endif +#ifndef INPUT_PULLDOWN +#define INPUT_PULLDOWN 0x09 +#endif +#ifndef OPEN_DRAIN +#define OPEN_DRAIN 0x10 +#endif +#ifndef OUTPUT_OPEN_DRAIN +#define OUTPUT_OPEN_DRAIN 0x13 +#endif +#ifndef ANALOG +#define ANALOG 0xC0 #endif // ============================ // 中断模式定义 // Interrupt Mode Definitions // ============================ +#ifndef DISABLED +#define DISABLED 0x00 +#endif #ifndef RISING -#define RISING 0x01 +#define RISING 0x01 #endif #ifndef FALLING -#define FALLING 0x02 +#define FALLING 0x02 +#endif +#ifndef CHANGE +#define CHANGE 0x03 +#endif +#ifndef ONLOW +#define ONLOW 0x04 +#endif +#ifndef ONHIGH +#define ONHIGH 0x05 +#endif +#ifndef ONLOW_WE +#define ONLOW_WE 0x0C +#endif +#ifndef ONHIGH_WE +#define ONHIGH_WE 0x0D #endif // ============================ @@ -654,6 +687,23 @@ public: bool setPwmDuty12bit(uint8_t channel, uint16_t duty12, bool polarity = false, bool enable = true); bool getPwmDuty12bit(uint8_t channel, uint16_t* duty12, bool* polarity, bool* enable); + /** + * @brief Arduino-compatible analogWrite function (PWM output) + * Arduino 兼容的 analogWrite 函数(PWM 输出) + * @param channel PWM channel (0-3): M5IOE1_PWM_CH1/CH2/CH3/CH4 + * PWM 通道(0-3):M5IOE1_PWM_CH1/CH2/CH3/CH4 + * CH1=IO9, CH2=IO8, CH3=IO11, CH4=IO10 + * @param value PWM duty cycle (0-255, 8-bit Arduino standard) + * PWM 占空比(0-255,8-bit Arduino 标准) + * 0 = 0% duty, 127 = 50% duty, 255 = 100% duty + * @return true if successful + * @note This function scales 8-bit value to 12-bit internally + * 此函数内部将 8-bit 值缩放到 12-bit + * @note Value 0 turns off PWM output + * 值为 0 时关闭 PWM 输出 + */ + bool analogWrite(uint8_t channel, uint8_t value); + // ======================== // NeoPixel LED 功能 // NeoPixel LED Functions