esp-idf 환경에서 m5 Stack 소프트웨어 개발 및 시작

32727 단어 m5stackesptech

목적

  • esp-idf 환경을 구축하여 m5 Stack을 이동합니다.
  • Arduino IDE보다 더 유연한 환경에서 쓰고 싶지만 아무것도 모르는 사람을 향해
  • esp-idf를 겸한 학습
  • esp-idf 환경에 M5GFX를 도입하여 화면 그리기 시연을 시작합니다.
  • 버튼 조작 및 사운드 재생
  • (가속도 센서 등은 별도로...)
  • 환경 버전

  • Ubuntu 20.04
  • m5stack core
  • (esp-idf)
    $ git log -n1
    commit 8bf14a9238329954c7c5062eeeda569529aedf75 (HEAD, tag: v4.3.2)
    Author: Ivan Grokhotkov <[email protected]>
    Date:   Mon Dec 20 19:02:41 2021 +0100
    
        versions: Update version to 4.3.2
        
    (M5GFX)
    $ git log -n1
    commit a8e406af056f3b1cb331c0b66126ab828197755e (HEAD -> master, tag: 0.0.15, origin/master, origin/HEAD)
    Merge: 41af34c c2370c0
    Author: lovyan03 <[email protected]>
    Date:   Sat Nov 27 12:21:25 2021 +0900
    
        Merge pull request #24 from m5stack/develop
        
        0.0.15
    

    절차.


    Install


    github에서clone esp-idf를 시작합니다.일반적으로 clone은 pull master를 하기 때문에 적당한 버전에서 체크아웃(본편에서는 이미 체크아웃v4.3.2합니다.esp-idf에submodule가 있기 때문에 체크아웃 시 잊지 마세요git submodule update[1].
    pyenv와 asdf를 가져오면psyhon의 처리를 주의하십시오.python 시스템 오류가 발생했을 때 pip install를 검사할 때 어디에 설치되었는지, ep-idf에서 어떤python을 사용했는지 등을 검사합니다.
    git clone https://github.com/espressif/esp-idf.git -b v4.3.2
    cd esp-idf.git
    ./install.sh
    

    Build hello_world

    idf.py를 사용하기 위해 export.sh에서 환경 변수를 읽습니다.이렇게 하면 idf.py 등을 사용할 수 있다.
    앞으로 빌딩과 기록 사용idf.py.
    source export.sh
    
    구축, 쓰기examples/get-started/hello_world를 시도합니다.
    cd examples/get-started/hello_world
    idf.py build
    
    다음과 같은 경우 성공
    Project build complete. To flash, run this command:
    /home/fai/.espressif/python_env/idf5.0_py3.8_env/bin/python ../../../components/esptool_py/esptool/esptool.py -p (PORT) -b 460800 --before default_reset --after hard_reset --chip esp32  write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin
    or run 'idf.py -p (PORT) flash'
    
    m5 Stack을 PC에 연결하여 디바이스 파일을 확인합니다.ls /dev/*USB*에서 대충 확인할 수 있습니다.
    $ ls /dev/*USB*
    /dev/ttyUSB0
    
    구축이 성공했을 때의 메시지에 따라 flash하면 기록합니다.편하네요.
    idf.py -p /dev/ttyUSB0 flash
    
    hello_월드가 직렬로 출력되기 때문에 monitor에서 직렬을 확인합니다."Serial을 종료하려면 누르기ctrl + ]"메시지가 표시되므로 Serial을 종료하려고 합니다[2].
    idf.py -p /dev/ttyUSB0 monitor
    
    다음 텍스트는 직렬로 수신해야 합니다.
    Hello world!
    This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision 1, 2MB external flash
    Minimum free heap size: 294012 bytes
    
    참고로 샘플은 printf을 사용하지만 로그를 직렬로 출력하려면 printf 대신 "esp_log.h"#includeESP_LOGI("TAG", "message v=%d", 5); 등을 사용하는 것이 좋다.
    또 지령을 나란히 쓸 수 있기 때문에 개발할 때 다음 명령을 자주 두드린다.
    idf.py -p /dev/ttyUSB0 build flash monitor
    
    기타 명령이 풍부하다.특히 사용이 가능한 것은 fullcleanmenuconfig 등이다.

    Import M5GFX


    https://github.com/m5stack/M5GFX.
    이동만 한다면 아래처럼components에 깊이 들어가면 됩니다.
    cd components
    git clone https://github.com/m5stack/M5GFX
    

    Run M5GFX samples


    esp-idf의 맨 윗부분 디렉터리로 돌아가서work 디렉터리를 만듭니다.이번에는 아래에서 프로젝트를 만들어 작업하기로 했다.hello_월드 복제, 혹시 모르니까 구축 가능.
    cp -ar examples/get-started/hello_world work/
    cd work/hello_world
    idf.py fullclean
    idf.py -p /dev/ttyUSB0 build flash monitor
    
    M5GFX는 C++입니다.hello_world_main.chello_world_main.cpp로 바꾸다.
    다시 쓰다work/hello_world/main/CMakeLists.txt.파일 이름 바꾸기 외에 M5GFX 를 추가하여 PRIV_REQUIRES "M5GFX" 에 의존하도록 했다.
    idf_component_register(SRCS "hello_world_main.cpp"
                           PRIV_REQUIRES "M5GFX"
                           INCLUDE_DIRS "")
    
    M5GFX의 샘플을 복사해 이동하고 싶었지만, 아두노 IDE를 겨냥한 샘플에만 들어갔기 때문에 자기 앞에서 호출setuploop로 실행했다.
    esp-idf는 임무 개념을 가지고 여러 코드를 병렬로 이동할 수 있다.app_main함수로도 잡도 무한순환을 할 수 있지만 얻기 어려워xTaskCreatePinnedToCore를 사용한다.
    ref: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html
    // hello_world_main.cpp
    
    // ============================================================================
    // BEGIN BarGraph.ino
    // https://github.com/m5stack/M5GFX
    // //examples/Basic/BarGraph/BarGraph.ino
    
    // FIXME: 下記URLのコードをここに貼り付ける
    // https://github.com/m5stack/M5GFX/blob/a8e406af056f3b1cb331c0b66126ab828197755e/examples/Basic/BarGraph/BarGraph.ino 
    
    // END BarGraph.ino
    // ============================================================================
    
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    
    TaskHandle_t g_handle = nullptr;
    
    void runMainLoop(void *args) {
      setup();
      for (;;) {
        loop();
        // avoid `The following tasks did not reset the watchdog in time`
        vTaskDelay(1);
      }
      vTaskDelete(g_handle);
    }
    
    void initializeTask() {
      xTaskCreatePinnedToCore(&runMainLoop, "task1-main", 8192, nullptr, 1,
                              &g_handle, 1);
      configASSERT(g_handle);
    }
    
    extern "C" {
    void app_main(void) {
      //
      initializeTask();
    }
    }
    
    idf.py -p /dev/ttyUSB0 build flash monitor에 기록을 구축하고 BarGraph의 시위 행진이 시작되면 ok.

    Implement M5GFX App


    또 시료에서 알아채기 어려운 M5GFX와 관련된 문제 등을 들었다.
  • 소음 발생
  • 깜박임
  • 잡음이 나다
    GPIO25 및 GND는 점프 라인을 통해 연결한 후 해제됩니다.아래의 보도는 매우 상세한 것 같다.
  • https://macsbug.wordpress.com/2019/09/27/m5stack-speaker-noise-reduction/
  • https://community.m5stack.com/topic/61/noise-on-speaker
  • 번뜩이다M5GFX의 드로잉 명령이 버퍼링되지 않습니다.M5Canvas를 사용하여 사이다를 써넣고 사이다를 붙여서 완충하는 것은 일반적인 상황인 것 같습니다.
    M5GFX g_display;
    M5Canvas g_canvas(&g_display);
    int g_time = 0;
    
    void setupDisplay() {
      g_display.init();
      g_display.startWrite();
    
      g_canvas.createSprite(g_display.width(), g_display.height());
    }
    
    void renderDisplay() {
      int kW = g_display.width();
      int kH = g_display.height();
      int kL = std::min(kW, kH) * 4 / 10;
    
      g_display.waitDisplay();
      g_canvas.fillRect(0, 0, kW, kH, TFT_BLACK);
    
      float a1 = 0.01 * g_time;
      float a2 = a1 + PI * 2 / 3;
      float a3 = a1 - PI * 2 / 3;
      g_canvas.fillTriangle(std::cos(a1) * kL + kW / 2, std::sin(a1) * kL + kH / 2,
                            std::cos(a2) * kL + kW / 2, std::sin(a2) * kL + kH / 2,
                            std::cos(a3) * kL + kW / 2, std::sin(a3) * kL + kH / 2,
                            TFT_WHITE);
    
      g_canvas.pushSprite(0, 0);
      g_display.display();
      g_time += 3;
    }
    
    위에 설명된 구현 방법 중에서도 유선이 들어오는 깜박임이 발생할 수 있습니다.그리는 과정에서 인터럽트가 삽입되어 있어서 그런지 적당히 라인을 양보하면 해제[3]됩니다.수중의 환경에서vTaskDelay(15 / portTICK_PERIOD_MS); 현상이 발생하여vTaskDelay(40 / portTICK_PERIOD_MS);에 억제되었다.

    Control GPIO (buttons, speaker)


    버튼과 스피커가 GPIO에 연결되어 있어 아두노와 랩베리피의 작업감각과 같다.새치기도 가능합니다.

    buttons

  • 버튼은 GPIO 39, 38, 37에 연결됩니다.
  • ref: https://zenn.dev/okuoku/scraps/ac9d4146bc1f8d
  • gpio_config에서 설정하고 gpio_get_level에서 값을 읽습니다.
  • ref: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html

  • 중단된 구현 사례는 examples/peripherals/gpio/에 포함됩니다.
  • ref: https://github.com/espressif/esp-idf/blob/a17cd247619ed7400fe09b308626818f8a448b9e/examples/peripherals/gpio/generic_gpio/main/gpio_example_main.c
  • 다음은 인터럽트를 사용하지 않고 함수 호출을 통해 값을 얻는 예입니다.
    void initialize() {
      gpio_config_t io_conf = {};
      io_conf.intr_type = GPIO_INTR_DISABLE;
      io_conf.pin_bit_mask =
          (1ull << GPIO_NUM_39) | (1ull << GPIO_NUM_38) | (1ull << GPIO_NUM_37);
      io_conf.mode = GPIO_MODE_INPUT;
      io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
      gpio_config(&io_conf);
    }
    
    std::array<bool, 3> update() {
      bool a = gpio_get_level(GPIO_NUM_39);
      bool b = gpio_get_level(GPIO_NUM_38);
      bool c = gpio_get_level(GPIO_NUM_37);
      return std::array<bool, 3>{a, b, c};
    }
    

    speaker

    driver/ledc.h를 사용하여 PWM 제어를 수행합니다.dutyhpoint에 대한 상세한 설명http://blueeyes.sakura.ne.jp/2021/01/31/3672/.공식 문서에는 ESP32 Technical Reference Manual LED PWM Controller 14개에 적혀 있습니다.
    void initialize() {
      gpio_config_t config;
      config.intr_type = GPIO_INTR_DISABLE;
      config.pin_bit_mask = (1ull << GPIO_NUM_25);
      config.mode = GPIO_MODE_OUTPUT;
      config.pull_up_en = GPIO_PULLUP_DISABLE;
      config.pull_down_en = GPIO_PULLDOWN_DISABLE;
      gpio_config(&config);
    }
    void setSound(uint32_t freq, bool enable) {
      ledc_timer_config_t timer_config = {};
      timer_config.speed_mode = LEDC_HIGH_SPEED_MODE;
      timer_config.duty_resolution = LEDC_TIMER_8_BIT;
      timer_config.timer_num = LEDC_TIMER_3;
      timer_config.freq_hz = freq;
      timer_config.clk_cfg = LEDC_AUTO_CLK;
      ledc_timer_config(&timer_config);
    
      ledc_channel_config_t channel_config = {};
      channel_config.gpio_num = GPIO_NUM_25;
      channel_config.speed_mode = LEDC_HIGH_SPEED_MODE;
      channel_config.channel = LEDC_CHANNEL_1;
      channel_config.intr_type = LEDC_INTR_DISABLE;
      channel_config.timer_sel = timer_config.timer_num;
      channel_config.duty = enable ? 0x7F : 0x00;
      channel_config.hpoint = 0x0;
      ledc_channel_config(&channel_config);
    }
    void runSpeakerLoop(void *args) {
      vTaskDelay(3000 / portTICK_PERIOD_MS);
      g_my_io.setSound(262, true);
      vTaskDelay(500 / portTICK_PERIOD_MS);
      g_my_io.setSound(294, true);
      vTaskDelay(500 / portTICK_PERIOD_MS);
      g_my_io.setSound(330, true);
      vTaskDelay(500 / portTICK_PERIOD_MS);
      g_my_io.setSound(330, false);
      vTaskDelete(nullptr);
    }
    
    그나저나 channel_config.channel = LEDC_CHANNEL_7라면 duty=0 모니터가 길이 되어 사라지거나 timer_config.clk_cfg = LEDC_AUTO_CLK 이외에 다른 동작이 없어 디테일을 파악하지 못하거나...만약 무슨 착오가 있으면 나중에 수정할 것이다.

    sample code


    동작을 확인할 때 사용하는 전체 이미지 코드
    https://gist.github.com/buyoh/01af1c1595ed3ce9356196e5f4222d1e
    각주
    까먹고 반했어.↩︎
    환경에 따라 다를 수 있음↩︎
    한마디로 집행M5Canvas#pushSprite 과정에서 새치기당하지 말아야 한다.M5Canvas#fillRect 등 사이에 새치기가 발생해도 문제가 없었다.나는 더 좋은 방법이 있다고 생각한다. 어쨌든 지금은 이렇다.↩︎

    좋은 웹페이지 즐겨찾기