goo blog サービス終了のお知らせ 

忘備録-備忘録

技術的な備忘録

ch32v003で電子オルゴール その2

2025-02-07 20:40:42 | ch32v003
A tiny MML parserを使用した電子オルゴールその2です。
発音にDDS方式で正弦波を発生させ、エンベローブのデータを掛け合わせ、少しでもオルゴールの音質を再現できるようにしました。DA変換にはPWM方式を用いています。出力値を矩形波のON-OFFから中間値(256段階)をとることができるので、和音の出力も可能です。
//                       CH32V003J4M6
//                          +------+
//  signal <-- TIM1_CH2/PA1-+1    8+-PD1/SWIO
//                      GND-+2    7+-PC4/TIM1_CH4 --> signal
//                      PA2-+3    6+-PC2
//                      VDD-+4    5+-PC1
//                          +------+
// 電子オルゴール
// A tiny MML parser(https://cubeatsystems.com/tinymml/)を使用
// song.hはA tiny MML parserの/src/ample/arduino_chord_exampleにあるもの
// Direct Digital Synthesizer (DDS)方式で発音する
// 和音再生可能
// サンプリング周波数 31250Hz

#include "ch32v003fun.h"
#include <stdio.h>
#include "mml.h"
#include "song.h"

// エンベローブデータ
const int8_t envData[] = 
{
    127,  60,  80,  90, 100, 110, 127, 127, 127, 127, 127, 110, 100,  90,  80,  75,
     73,  71,  69,  67,  65,  63,  61,  59,  57,  55,  54,  52,  50,  48,  47,  45,
     43,  42,  40,  39,  37,  36,  34,  33,  31,  30,  29,  27,  26,  25,  24,  22,
     21,  20,  19,  18,  17,  16,  15,  14,  13,  12,  11,  10,  10,   9,   8,   7,
      7,   6,   6,   5,   4,   4,   3,   3,   2,   2,   2,   1,   1,   0
};
// sin波テーブル
const int8_t sinData[256] = 
{
    0,     3,    6,    9,   12,   15,   18,   21,   24,   28,   31,   34,   37,   40,   43,   46,
   48,    51,   54,   57,   60,   63,   65,   68,   71,   73,   76,   78,   81,   83,   85,   88,
   90,    92,   94,   96,   98,  100,  102,  104,  106,  107,  109,  111,  112,  114,  115,  116,
   118,  119,  120,  121,  122,  123,  123,  124,  125,  125,  126,  126,  126,  127,  127,  127,
   127,  127,  127,  127,  126,  126,  125,  125,  124,  124,  123,  122,  121,  120,  119,  118,
   117,  116,  114,  113,  111,  110,  108,  107,  105,  103,  101,   99,   97,   95,   93,   91,
    89,   87,   84,   82,   79,   77,   74,   72,   69,   67,   64,   61,   58,   56,   53,   50,
    47,   44,   41,   38,   35,   32,   29,   26,   23,   20,   17,   14,   10,    7,    4,    1,
    -2,   -5,   -8,  -11,  -15,  -18,  -21,  -24,  -27,  -30,  -33,  -36,  -39,  -42,  -45,  -48,
   -51,  -54,  -57,  -59,  -62,  -65,  -68,  -70,  -73,  -75,  -78,  -80,  -83,  -85,  -88,  -90,
   -92,  -94,  -96,  -98, -100, -102, -104, -106, -108, -109, -111, -112, -114, -115, -117, -118,
  -119, -120, -121, -122, -123, -124, -125, -125, -126, -126, -127, -127, -128, -128, -128, -128,
  -128, -128, -128, -127, -127, -127, -126, -126, -125, -124, -124, -123, -122, -121, -120, -119,
  -117, -116, -115, -113, -112, -110, -108, -107, -105, -103, -101,  -99,  -97,  -95,  -93,  -91,
   -89,  -86,  -84,  -82,  -79,  -77,  -74,  -72,  -69,  -66,  -64,  -61,  -58,  -55,  -52,  -49,
   -47,  -44,  -41,  -38,  -35,  -32,  -29,  -25,  -22,  -19,  -16,  -13,  -10,   -7,   -4,    0,
};
// 音階データ
const uint16_t notefreq[] = 
{
       67,   71,   75,   79,   84,   89,   94,  100,  106,  112,  119,  126,
      133,  141,  150,  159,  168,  178,  189,  200,  212,  224,  238,  252,
      267,  283,  299,  317,  336,  356,  377,  400,  423,  448,  475,  503,
      533,  565,  599,  634,  672,  712,  754,  799,  847,  897,  950, 1007,
     1067, 1130, 1197, 1268, 1344, 1424, 1508, 1598, 1693, 1794, 1900, 2013,
     2133, 2260, 2394, 2537, 2688, 2848, 3017, 3196, 3386, 3588, 3801, 4027,
     4266, 4520, 4789, 5074, 5375, 5695, 6034, 6392, 6773, 7175, 7602, 8054,
     8533, 9040, 9578,10147,10751,11390,12067,12785,13545,14351,15204,16108,
    17066,18081,19156,20295,21502,22780,24135,25570,27090,28701,30408,32216,
};

typedef struct
{
    uint32_t bpm;
    uint32_t bticks;
} NOTE;

// TIM1をコPWMモードで初期化
// Output pin CH4(PC4)
#define TIM1_DEFAULT 0x00
void t1pwm_init(void)
{
    // クロック供給 GPIOC and TIM1
    RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1;
    // PC4 is T1CH4, 10MHz Output alt func, push-pull
    GPIOC->CFGLR &= ~(0xf << (4 * 4));
    GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
    GPIOA->CFGLR &= ~(0xf << (4 * 1));
    GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 1); // TIM1のリセット
    RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
    RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;

    // SMCFGR: 入力の初期値 CK_INT
    // プリスケーラ設定 分周比は設定値+1
    TIM1->PSC = 5;
    // PWM幅の設定 8bit
    TIM1->ATRLR = 255;
    // 48MHz/(5+1)(PSC)/256(ATRLR) = 31250Hz PWM周波数

    // TIM_OC4M_2 | TIM_OC4M_1 PWM1モード設定
    // TIM_OC4PE オートリロード設定
    TIM1->CHCTLR1 |= TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2PE;
    TIM1->CHCTLR2 |= TIM_OC4M_2 | TIM_OC4M_1 | TIM_OC4PE;
    // ATRLRレジスタ オートリロード
    TIM1->CTLR1 |= TIM_ARPE;

    // Enable Channel outputs, set default state (based on TIM1_DEFAULT)
    TIM1->CCER |= TIM_CC4E | (TIM_CC4P & TIM1_DEFAULT);
    TIM1->CCER |= TIM_CC2E | (TIM_CC2P & ~TIM1_DEFAULT);
    // アップデートで割り込み発生
    TIM1->DMAINTENR |= TIM_UIE;
    // TIM1割り込み許可
    NVIC_EnableIRQ(TIM1_UP_IRQn);
    // 設定反映
    TIM1->SWEVGR |= TIM_UG;
    // TIM1 出力許可
    TIM1->BDTR |= TIM_MOE;
    // 出力変化しない
    TIM1->CH4CVR = TIM1->ATRLR + 1;
    TIM1->CH2CVR = TIM1->ATRLR + 1;
    // TIM2スタート
    TIM1->CTLR1 |= TIM_CEN;
}

// 最大同時発音数
#define MAX_NOTES 4
// 周波数の増加分
volatile uint16_t FrqDiff[MAX_NOTES];
// 発音時間 エンベローブ計算用
volatile uint16_t otime[MAX_NOTES];

// タイマ割り込み
void TIM1_UP_IRQHandler(void) __attribute__((interrupt));
void TIM1_UP_IRQHandler(void)
{
    // Clear the trigger state for the next IRQ
    TIM1->INTFR = 0x00000000;

    uint8_t phase, envc;
    static uint16_t sinCount[MAX_NOTES];
    int datasum = 0;

    for(int i=0;i<MAX_NOTES;i++) {
        sinCount[i] = sinCount[i] + FrqDiff[i];
        if (FrqDiff[i]==0)
            sinCount[i] = 0;
        phase = sinCount[i] >> 8; // 位相はsinCountの上位8bit
                                          // phaseは256あるのでオーバーフローしない
        envc = otime[i] >> 8;
        if (envc < 78)
        {
            datasum += ((int)envData[envc]) * ((int)sinData[phase]);
        }
        otime[i]++;
    }
    datasum = datasum >> 6; //ボリューム調整
    if (datasum > 127)
    {
        datasum = 127;
    }
    else if (datasum < -128)
    {
        datasum = -128;
    }
    TIM1->CH4CVR = TIM1->CH2CVR = 128 + datasum;
}

// 設定した周波数の矩形波(デューティ50%)を出力
//   frq : 周波数
//   tim : 出力する時間[msec]
void tone(uint32_t freq, uint32_t tim)
{
    static uint16_t freqlist[MAX_NOTES];
    static int count = 0;
    if (freq == 0)
    {
        // 出力停止 無音
        for (int i = 0; i < MAX_NOTES; i++)
        {
            FrqDiff[i] = 0;
            otime[i] = 0;
        }
        Delay_Ms(tim);
        count = 0;
        return;
    }
    if (tim == 0)
    {
        if (count < MAX_NOTES)
        {
            freqlist[count] = freq;
            count++;
        }
    }
    else
    {
        // 出力周波数設定
        if (count == 0) // 単音
        {
            for (int i = 0; i < MAX_NOTES; i++)
            {
                FrqDiff[i] = 0;
                otime[i] = 0;
            }
            FrqDiff[0] = freq;
        }
        else            // 和音
        {
            for (int i = 0; i < MAX_NOTES; i++)
            {
                FrqDiff[i] = freqlist[i];
                otime[i] = 0;
            }
        }
    }
    Delay_Ms(tim);
    // 出力停止
    for (int i = 0; i < MAX_NOTES; i++)
    {
        FrqDiff[i] = 0;
        freqlist[i] = 0;
    }
    count = 0;
}

static uint32_t get_note_length_ms(NOTE *p, uint32_t note_ticks)
{
    return (60000) * note_ticks / p->bpm / p->bticks;
}

void note_init(NOTE *p, int bpm, int bticks)
{
    p->bpm = bpm;
    p->bticks = bticks;
}

void note_tone(NOTE *p, int number, int ticks)
{
    uint16_t freq = notefreq[number];
    uint32_t ms = get_note_length_ms(p, ticks);
    tone(freq, ms);
}

void note_rest(NOTE *p, int ticks)
{
    uint32_t ms = get_note_length_ms(p, ticks);
    tone(0, ms);
}

NOTE note;
MML mml;
MML_OPTION mml_opt;

static void mml_callback(MML_INFO *p, void *extobj)
{
    switch (p->type)
    {
    case MML_TYPE_TEMPO:
    {
        MML_ARGS_TEMPO *args = &(p->args.tempo);
        note_init(&note, args->value, mml_opt.bticks);
    }
    break;
    case MML_TYPE_NOTE:
    {
        MML_ARGS_NOTE *args = &(p->args.note);
        note_tone(&note, args->number, args->ticks);
    }
    break;
    case MML_TYPE_REST:
    {
        MML_ARGS_REST *args = &(p->args.rest);
        note_rest(&note, args->ticks);
    }
    break;
    default:
        break;
    }
}

int main(void)
{
    SystemInit();
    Delay_Ms(100);
    t1pwm_init();

    // Initialize the MML module.
    mml_init(&mml, mml_callback, 0);
    MML_OPTION_INITIALIZER_DEFAULT(&mml_opt);
    // Initialize the note module.
    int tempo_default = 120;
    note_init(&note, tempo_default, mml_opt.bticks);

    while (1)
    {
        const char *song_table[] = {
            song_over_world,
            song_level_clear,
        };
        int i;

        for (i = 0; i < sizeof(song_table) / sizeof(song_table[0]); i++)
        {
            // Setup the MML module.
            mml_setup(&mml, &mml_opt, (char *)song_table[i]);
            // Fetch the MML text command.
            while (mml_fetch(&mml) == MML_RESULT_OK)
            {
            }
            Delay_Ms(1000);
        }
    }
}

参考

ch32v003で電子オルゴール

2025-01-28 21:53:34 | ch32v003
A tiny MML parserを使用して電子オルゴールを作成してみました。

  1. // CH32V003J4M6 pin map
  2. //     +------+
  3. //    -+1    8+-SWIO
  4. // GND-+2    7+-PC4/TIM1_CH4
  5. //    -+3    6+-
  6. // VDD-+4    5+-
  7. //     +------+

  8. #include "ch32v003fun.h"
  9. #include <stdio.h>
  10. #include "mml.h"
  11. #include "note.h"
  12. #include "song.h"

  13. const unsigned int sys_clock = 48000000; // System clock(48kHz)
  14. const unsigned int tim1_psc = 8;         // プリスケーラ

  15. // TIM1をコンペアアプトプットモードで初期化
  16. // Output pin CH4(PC4)
  17. void t1com_init(void)
  18. {
  19.     // クロック供給 GPIOC and TIM1
  20.     RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1;
  21.     // PC4 is T1CH4, 10MHz Output alt func, push-pull
  22.     GPIOC->CFGLR &= ~(0xf << (4 * 4));
  23.     GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
  24.     // TIM1のリセット
  25.     RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
  26.     RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
  27.     // ATRLRレジスタ オートリロード
  28.     TIM1->CTLR1 |= TIM_ARPE;
  29.     // プリスケーラ設定
  30.     TIM1->PSC = tim1_psc;
  31.     // カウンタ値の上限設定
  32.     TIM1->ATRLR = 1023;
  33.     // データ(ATRLR)の手動リロード 初期化
  34.     TIM1->SWEVGR |= TIM_UG;
  35.     // CH4 正側で出力
  36.     TIM1->CCER |= TIM_CC4E | TIM_CC4P;
  37.     // CH4 コンペアマッチでトグル出力(CC4S = 00, OC4M = 011)
  38.     TIM1->CHCTLR2 |= TIM_OC4M_0 | TIM_OC4M_1;
  39.     // コンペアマッチ値の指定 ATRLRより大きくするとマッチしない(出力が変化しない)
  40.     TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  41.     // TIM1 出力許可
  42.     TIM1->BDTR |= TIM_MOE;
  43.     // TIM1 動作開始
  44.     TIM1->CTLR1 |= TIM_CEN;
  45. }

  46. // 設定した周波数の矩形波(デューティ50%)を出力
  47. // frq : 周波数
  48. // tim : 出力する時間[msec]
  49. void tone(unsigned int frq, unsigned int tim)
  50. {
  51.     if (frq != 0)
  52.     {
  53.         // 出力周波数設定
  54.         TIM1->ATRLR = sys_clock / frq / tim1_psc / 2 - 1;
  55.         TIM1->CH4CVR = 0x0;
  56.     }
  57.     else
  58.     {
  59.         // 出力停止
  60.         TIM1->ATRLR = 1023;
  61.         TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  62.     }
  63.     Delay_Ms(tim);
  64.     // 出力停止
  65.     TIM1->ATRLR = 1023;
  66.     TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  67. }

  68. #define SOUND_SYSTEM_TONE(FREQ, MS) do { tone((FREQ),(MS)); } while (0)
  69. #define SOUND_SYSTEM_REST(MS) do { tone( 0 ,(MS)); } while (0)

  70. const uint16_t notefreq[] = {
  71.      32, 34, 36, 38, 41, 43, 46, 48, 51, 55, 58, 61,
  72.      65, 69, 73, 77, 82, 87, 92, 97, 103, 110, 116, 123,
  73.     130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
  74.     261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
  75.     523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
  76.    1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
  77.    2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951,
  78.    4186, 4434, 4698, 4978, 5274, 5587, 5919, 6271, 6644, 7040, 7458, 7902,
  79.    8372, 8869, 9397, 9956, 10548, 11175, 11839, 12543, 13289, 14080, 14917, 15804,
  80.   16744, 17739, 18794, 19912, 21096, 22350, 23679, 25087, 26579, 28160, 29834, 31608,
  81.   33488, 35479, 37589, 39824, 42192, 44701, 47359, 50175,
  82. };

  83. static uint32_t get_note_length_ms(NOTE *p, uint32_t note_ticks)
  84. {
  85.   return (60000) * note_ticks / p->bpm / p->bticks;
  86. }

  87. void note_init(NOTE *p, int bpm, int bticks)
  88. {
  89.   p->bpm = bpm;
  90.   p->bticks = bticks;
  91. }

  92. void note_tone(NOTE *p, int number, int ticks)
  93. {
  94.   uint16_t freq = notefreq[number];
  95.   uint32_t ms = get_note_length_ms(p, ticks);
  96.   SOUND_SYSTEM_TONE(freq, ms);
  97. }

  98. void note_rest(NOTE *p, int ticks)
  99. {
  100.   uint32_t ms = get_note_length_ms(p, ticks);
  101.   SOUND_SYSTEM_REST(ms);
  102. }

  103. NOTE note;
  104. MML mml;
  105. MML_OPTION mml_opt;

  106. static void mml_callback(MML_INFO *p, void *extobj)
  107. {
  108.     switch (p->type)
  109.     {
  110.     case MML_TYPE_TEMPO:
  111.     {
  112.         MML_ARGS_TEMPO *args = &(p->args.tempo);
  113.         note_init(&note, args->value, mml_opt.bticks);
  114.     }
  115.     break;
  116.     case MML_TYPE_NOTE:
  117.     {
  118.         MML_ARGS_NOTE *args = &(p->args.note);
  119.         note_tone(&note, args->number, args->ticks);
  120.     }
  121.     break;
  122.     case MML_TYPE_REST:
  123.     {
  124.         MML_ARGS_REST *args = &(p->args.rest);
  125.         note_rest(&note, args->ticks);
  126.     }
  127.     break;
  128.     }
  129. }

  130. void setup()
  131. {
  132.     // Initialize the MML module. 
  133.     mml_init(&mml, mml_callback, 0);
  134.     MML_OPTION_INITIALIZER_DEFAULT(&mml_opt);
  135.      // Initialize the note module.
  136.     int tempo_default = 120;
  137.     note_init(&note, tempo_default, mml_opt.bticks);
  138. }

  139. void loop()
  140. {
  141.     const char *song_table[] = {
  142.         song_over_world,
  143.         song_level_clear,
  144.     };
  145.     int i;

  146.     for (i = 0; i < sizeof(song_table) / sizeof(song_table[0]); i++)
  147.     {
  148.          // Setup the MML module.
  149.         mml_setup(&mml, &mml_opt, (char *)song_table[i]);
  150.          // Fetch the MML text command.
  151.         while (mml_fetch(&mml) == MML_RESULT_OK)
  152.         {
  153.         }
  154.     }
  155. }

  156. int main(void)
  157. {
  158.     SystemInit();
  159.     Delay_Ms(100);
  160.     t1com_init();

  161.     setup();
  162.     while (1)
  163.     {
  164.         loop();
  165.     }
  166. }


参考


CH32V003でPWM出力

2025-01-28 19:28:54 | ch32v003
CH32V003J4M6を使用してPWMを出力するプログラムです。
 
  1. // CH32V003J4M6 pin map
  2. //     +-------+
  3. //    -+ 1   8 +-SWIO
  4. // GND-+ 2   7 +-
  5. //    -+ 3   6 +-PC2/TIM2_CH2
  6. // VDD-+ 4   5 +-PC1/TIM2_CH4
  7. //     +-------+

  8. // Timer 2 pin mappings by AFIO->PCFR1
  9. // 00 (default)
  10. // D4        T2CH1ETR
  11. // D3        T2CH2
  12. // C0        T2CH3
  13. // D7        T2CH4
  14. // 01 <--このピン配置を使用する
  15. // C5        T2CH1ETR_
  16. // C2        T2CH2_
  17. // D2        T2CH3_
  18. // C1        T2CH4_
  19. // 10
  20. // C1        T2CH1ETR_
  21. // D3        T2CH2
  22. // C0        T2CH3
  23. // D7        T2CH4
  24. // 11
  25. // C1        T2CH1ETR_
  26. // C7        T2CH2_
  27. // D6        T2CH3_
  28. // D5        T2CH4_

  29. #include "ch32v003fun.h"
  30. #include <stdio.h>

  31. // mask for the CCxP bits
  32. // when set PWM outputs are held HIGH by default and pulled LOW
  33. // when zero PWM outputs are held LOW by default and pulled HIGH
  34. #define TIM2_DEFAULT 0xff
  35. // #define TIM2_DEFAULT 0x00

  36. void t2pwm_init(void)
  37. {
  38.     // Enable GPIOD and TIM2
  39.     RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO; // RCC_APB2Periph_AFIO ピンを配置変更する場合
  40.     RCC->APB1PCENR |= RCC_APB1Periph_TIM2;

  41.     // ピンの再マップ
  42.     AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_PARTIALREMAP1;

  43.     // PC1 is T2CH4, 10MHz Output alt func, push-pull
  44.     GPIOC->CFGLR &= ~(0xf << (4 * 1));
  45.     GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 1);
  46.     // PC2 is T2CH2, 10MHz Output alt func, push-pull
  47.     GPIOC->CFGLR &= ~(0xf << (4 * 2));
  48.     GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 2);

  49.     // Reset TIM2 to init all regs
  50.     RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
  51.     RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;

  52.     // SMCFGR: 入力の初期値 CK_INT
  53.     // プリスケーラ設定 分周比は設定値+1
  54.     TIM2->PSC = 5;
  55.     // PWM幅の設定 8bit
  56.     TIM2->ATRLR = 255;
  57.     // 48MHz/(5+1)(PSC)/256(ATRLR) = 31250Hz PWM周波数

  58.     // TIM_OC2M_2 | TIM_OC2M_1 PWM1モード設定
  59.     // TIM_OC2PE オートリロード設定
  60.     TIM2->CHCTLR1 |= TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2PE;
  61.     TIM2->CHCTLR2 |= TIM_OC4M_2 | TIM_OC4M_1 | TIM_OC4PE;
  62.     // ATRLRレジスタ オートリロード
  63.     TIM2->CTLR1 |= TIM_ARPE;

  64.     // 出力許可, 初期値はTIM2_DEFAULTで決まる
  65.     // TIM2->CCER |= TIM_CC1E | (TIM_CC1P & TIM2_DEFAULT);
  66.     TIM2->CCER |= TIM_CC2E | (TIM_CC2P & TIM2_DEFAULT);
  67.     TIM2->CCER |= TIM_CC4E | (TIM_CC4P & TIM2_DEFAULT);
  68.  
  69.      // 設定反映
  70.     TIM2->SWEVGR |= TIM_UG;

  71.     // TIM2動作開始
  72.     TIM2->CTLR1 |= TIM_CEN;
  73. }

  74. int main()
  75. {
  76.     uint32_t count = 0;

  77.     SystemInit();
  78.     Delay_Ms(100);

  79.     // init TIM2 for PWM
  80.     t2pwm_init();

  81.     while (1)
  82.     {
  83.         TIM2->CH2CVR = count; // CH2
  84.         TIM2->CH4CVR = (count + 128) & 255; // CH4
  85.         count++;
  86.         count &= 255;
  87.         Delay_Ms(5);
  88.     }
  89. }


参考

CH32V003J4M6で矩形波の出力

2025-01-20 20:40:08 | ch32v003
指定した周波数の信号を出力するプログラムです。CH32V003J4M6を使用しTIM1のCH4を利用しています。信号の出力はPC4になります。

 
  1. #include "ch32v003fun.h"
  2. #include <stdio.h>

  3. const unsigned int sys_clock = 48000000; // System clock(48kHz)
  4. const unsigned int tim1_psc = 8; // プリスケーラ

  5. // TIM1をコンペアアプトプットモードで初期化
  6. // Output pin CH4(PC4)
  7. void t1com_init(void)
  8. {
  9.     // クロック供給 GPIOC and TIM1
  10.     RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1;
  11.     // PC4 is T1CH4, 10MHz Output alt func, push-pull
  12.     GPIOC->CFGLR &= ~(0xf << (4 * 4));
  13.     GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF) << (4 * 4);
  14.     // TIM1のリセット
  15.     RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
  16.     RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
  17.     // プリスケーラ設定
  18.     TIM1->PSC = tim1_psc;
  19.     // カウンタ値の上限設定
  20.     TIM1->ATRLR = 1023;
  21.     // データ(ATRLR)のリロード
  22.     TIM1->SWEVGR |= TIM_UG;
  23.     // CH4 正側で出力
  24.     TIM1->CCER |= TIM_CC4E | TIM_CC4P;
  25.     // CH4 コンペアマッチでトグル出力(CC4S = 00, OC4M = 011)
  26.     TIM1->CHCTLR2 |= TIM_OC4M_0 | TIM_OC4M_1;
  27.     // コンペアマッチ値の指定 ATRLRより大きくするとマッチしない(出力が変化しない)
  28.     TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  29.     // TIM1 出力許可
  30.     TIM1->BDTR |= TIM_MOE;
  31.     // TIM1 動作開始
  32.     TIM1->CTLR1 |= TIM_CEN;
  33. }

  34. // 設定した周波数の矩形波(デューティ50%)を出力
  35. // frq : 周波数
  36. // tim : 出力する時間[msec]
  37. void tone(unsigned int frq, unsigned int tim)
  38. {
  39.     if (frq != 0)
  40.     {
  41.         // 出力周波数設定
  42.         TIM1->ATRLR = sys_clock / frq / tim1_psc / 2 - 1;
  43.         TIM1->CH4CVR = 0x0;
  44.         TIM1->SWEVGR |= TIM_UG; // データのアップデート
  45.     }
  46.     else
  47.     {
  48.         // 出力停止
  49.         TIM1->ATRLR = 1023;
  50.         TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  51.         TIM1->SWEVGR |= TIM_UG;
  52.     }
  53.     Delay_Ms(tim);
  54.     // 出力停止
  55.     TIM1->ATRLR = 1023;
  56.     TIM1->CH4CVR = TIM1->ATRLR + 1; // no compare macth
  57.     TIM1->SWEVGR |= TIM_UG;
  58. }

  59. int main()
  60. {

  61.     SystemInit();
  62.     Delay_Ms(100);

  63.     // init TIM1 (PC4=pin7 for J4m6)
  64.     t1com_init();

  65.     while (1)
  66.     {
  67.         tone(523 / 2, 300); // ド
  68.         // tone(554/2, 300);// ド♯
  69.         tone(587 / 2, 300); // レ
  70.         // tone(622/2, 300);// レ♯
  71.         tone(659 / 2, 300); // ミ
  72.         tone(698 / 2, 300); // ファ
  73.         // tone(740/2, 300);// ファ♯
  74.         tone(784 / 2, 300); // ソ
  75.         // tone(831/2, 300);// ソ♯
  76.         tone(880 / 2, 300); // ラ
  77.         // tone(932/2, 300);// ラ♯
  78.         tone(988 / 2, 300); // シ
  79.         tone(523, 300); // ド
  80.         // tone(554, 300); // ド♯
  81.         tone(587, 300); // レ
  82.         // tone(622, 300); // レ♯
  83.         tone(659, 300); // ミ
  84.         tone(698, 300); // ファ
  85.         // tone(740, 300); // ファ♯
  86.         tone(784, 300); // ソ
  87.         // tone(831, 300); // ソ♯
  88.         tone(880, 300); // ラ
  89.         // tone(932, 300); // ラ♯
  90.         tone(988, 300); // シ
  91.         tone(0, 1000); // 無音
  92.     }
  93. }

参考URL

時間関連の関数

2025-01-20 18:44:19 | ch32v003
ch32v003funでArduinoの時間関係の関数が使用できるようにするためのプログラムです。サンプルプログラムをほんの少し書き換えたものです。
  • millis()
  • micros()
  • delay()
  • delayMicroseconds()
が使用可能になります。

 
  1. // Example for using SysTick with IRQs
  2. // 03-25-2023 E. Brombaugh
  3. // 05-12-2023 C. Lohr (Modified to reflect updated sysclk)
  4. // 09-25-2024 ADBeta (Minor updates to main loop, comments and added
  5. // convenient macro function)

  6. #include "ch32v003fun.h"
  7. #include <stdio.h>

  8. // Number of ticks elapsed per millisecond (48,000 when using 48MHz Clock)
  9. #define SYSTICK_ONE_MILLISECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000 / 8)
  10. // Number of ticks elapsed per microsecond (48 when using 48MHz Clock)
  11. #define SYSTICK_ONE_MICROSECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000000 / 8)

  12. // Simple macro functions to give a arduino-like functions to call
  13. // millis() reads the incremented systick variable
  14. // micros() reads the raw SysTick Count, and divides it by the number of
  15. // ticks per microsecond ( WARN: Wraps every 90 seconds!)
  16. #define millis() (systick_millis)
  17. #define micros() (SysTick->CNT / SYSTICK_ONE_MICROSECOND)

  18. #define delay Delay_Ms
  19. #define delayMicroseconds Delay_Us

  20. // Incremented in the SysTick IRQ - in this example once per millisecond
  21. volatile uint32_t systick_millis;

  22. // Initialises the SysTick to trigger an IRQ with auto-reload, using HCLK/8 as
  23. // its clock source
  24. void systick_init(void)
  25. {
  26.     // Reset any pre-existing configuration
  27.     SysTick->CTLR = 0x0000;

  28.     // Set the compare register to trigger once per millisecond
  29.     SysTick->CMP = SYSTICK_ONE_MILLISECOND - 1;

  30.     // Reset the Count Register, and the global millis counter to 0
  31.     SysTick->CNT = 0x00000000;
  32.     systick_millis = 0x00000000;

  33.     // Set the SysTick Configuration
  34.     // NOTE: By not setting SYSTICK_CTLR_STRE, we maintain compatibility with
  35.     // busywait delay funtions used by ch32v003_fun.
  36.     SysTick->CTLR |= SYSTICK_CTLR_STE | // Enable Counter
  37.                                         // SYSTICK_CTLR_STRE <- Enable auto reload
  38.                      SYSTICK_CTLR_STIE; // Enable Interrupts
  39.                                         // SYSTICK_CTLR_STCLK <- HCLK/1; // Set Clock Source to HCLK/8

  40.     // Enable the SysTick IRQ
  41.     NVIC_EnableIRQ(SysTicK_IRQn);
  42. }

  43. // SysTick ISR - must be lightweight to prevent the CPU from bogging down.
  44. // Increments Compare Register and systick_millis when triggered (every 1ms)
  45. // NOTE: the `__attribute__((interrupt))` attribute is very important
  46. void SysTick_Handler(void) __attribute__((interrupt));
  47. void SysTick_Handler(void)
  48. {
  49.     // Increment the Compare Register for the next trigger
  50.     // If more than this number of ticks elapse before the trigger is reset,
  51.     // you may miss your next interrupt trigger
  52.     // (Make sure the IQR is lightweight and CMP value is reasonable)
  53.     SysTick->CMP += SYSTICK_ONE_MILLISECOND;

  54.     // Clear the trigger state for the next IRQ
  55.     SysTick->SR = 0x00000000;

  56.     // Increment the milliseconds count
  57.     systick_millis++;
  58. }

  59. int main(void)
  60. {
  61.     SystemInit();
  62.     delay(100);

  63.     printf("\n\nsystick_irq example\n\r");

  64.     // Initialise the IRQ
  65.     printf("initializing systick...");
  66.     systick_init();
  67.     printf("done.\n\r");

  68.     // Enable GPIOs for demonstration
  69.     funGpioInitAll();
  70.     funPinMode(PA2, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
  71.     funPinMode(PC1, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);
  72.     funPinMode(PC2, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP);

  73.     printf("Beginning Loop...\n\r");
  74.     while (1)
  75.     {
  76.         // Toggle the GPIO Pins with a delay - total delay will be 500ms
  77.         uint32_t start_millis = millis();
  78.         // On
  79.         funDigitalWrite(PA2, FUN_HIGH);
  80.         funDigitalWrite(PC1, FUN_HIGH);
  81.         funDigitalWrite(PC2, FUN_HIGH);
  82.         delay(500);
  83.         // Off
  84.         funDigitalWrite(PA2, FUN_LOW);
  85.         funDigitalWrite(PC1, FUN_LOW);
  86.         funDigitalWrite(PC2, FUN_LOW);
  87.         delay(500);
  88.         uint32_t end_millis = millis();

  89.         // NOTE: Due to the time it takes for printf(), the Current Millis will
  90.         // increment more than 500 per loop
  91.         printf("\nMilliseconds taken:\t%lu\n\r", end_millis - start_millis);
  92.         printf("Current Milliseconds:\t%lu\n\r", millis());
  93.         printf("Current Microseconds:\t%lu\n\r", micros());
  94.         printf("SysTick->CNT:\t\t%lu\n\r", SysTick->CNT);
  95.     }
  96. }


参考URL