Răsfoiți Sursa

add README.md

Satoshi Yoneda 1 lună în urmă
părinte
comite
69e5aa067f

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "cmake.sourceDirectory": "D:/Users/yoneda/Documents/Pico_Projects/Pico_C_Book/adc_dma_sample"
+}

+ 3 - 0
adc_dma_sample/README.md

@@ -0,0 +1,3 @@
+# 第6章リスト3 adc_dma_sample
+
+掲載時のコードに対して、バッファの読み書きが重ならないように `read_buf_selector`をグローバルにしてリードライト互いに反転させることで、多少の頑強さを加えていますが、時間経過に伴いリードライトの歩調が合わなくなる可能性は変わりません。

+ 2 - 2
adc_dma_sample/adc_dma_sample.c

@@ -30,12 +30,13 @@ int adc_dma_ch;
 
 volatile static uint8_t rw_buffer[2][RW_BUFFER_SIZE];
 volatile static int write_buf_selector = 0;
+volatile static int read_buf_selector = 1;
 
 void adc_dma_irq_handler(void)
 {
     // 割り込みフラグクリア
     dma_channel_acknowledge_irq0(adc_dma_ch);
-    write_buf_selector ^= 1;
+    write_buf_selector = read_buf_selector ^ 1;
     // DMA転送を再トリガー
     dma_channel_set_write_addr(adc_dma_ch, rw_buffer[write_buf_selector], false);
     dma_channel_set_trans_count(adc_dma_ch, RW_BUFFER_SIZE, true);     
@@ -44,7 +45,6 @@ void adc_dma_irq_handler(void)
 void pwm_irq_handler(void)
 {
     static int counter = 0;
-    static int read_buf_selector = 1;
     pwm_clear_irq(slice_num);
     pwm_set_gpio_level(LED_PIN, (uint16_t)rw_buffer[read_buf_selector][counter++]);
     if(counter > RW_BUFFER_SIZE) {

+ 3 - 0
adc_sample/README.md

@@ -0,0 +1,3 @@
+第6章リスト2 adc_irq_sample
+
+掲載時のソースと概ね同じです。

+ 0 - 3
adc_sample/adc_sample.c

@@ -23,15 +23,12 @@ typedef enum {
 #define PWM_OUT_PIN PICO_DEFAULT_LED_PIN
 #define PWM_SUB_PIN 2
 
-
-
 void adc_irq_handler(void)
 {
     while(!adc_fifo_is_empty()) {
         uint16_t value = adc_fifo_get() & 0xFFF;
         pwm_set_gpio_level(PWM_OUT_PIN, value);
         pwm_set_gpio_level(PWM_SUB_PIN, value);
-
     }
 }
 

+ 3 - 0
i2c_slave_sample/README.md

@@ -0,0 +1,3 @@
+# 第7章リスト7 I2C接続ADCのサンプル
+
+掲載時のコードに若干の整理を加えましたが、ほぼ変わりはありません。

+ 6 - 4
i2c_slave_sample/i2c_slave_sample.c

@@ -37,11 +37,12 @@ bool i2c_slave_request(i2c_inst_t *i2c, int cmd)
         uint32_t volt;
         adc_select_input(ADC_CH);
         value = adc_read();
-        volt = value * 33000 / 4096;
-        // MSB First
+
         if(cmd == CMD_GET_VOLT) {
-            value = volt;
+            // 3.3V ref, 12-bit ADC(0-4095)
+            value = (uint32_t)value * ADC_REF / 4095;
         }
+        // RP2040はリトルエンディアンのため、LSBから送信される
         i2c_write_raw_blocking(i2c, (uint8_t *)&value, sizeof(uint16_t));
         rval = true;
         break;
@@ -89,7 +90,7 @@ int main()
 {
     stdio_init_all();
 
-    // I2C Initialisation. Using it at 400Khz.
+    // I2C Initialisation. Using it at 100Khz.
     i2c_init(i2c0, 100*1000);
     
     gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
@@ -106,3 +107,4 @@ int main()
         tight_loop_contents();
     }
 }
+

+ 3 - 0
pio_dht11/README.md

@@ -0,0 +1,3 @@
+# 第4章リスト5~6
+
+PIOを使用してDHT-11から測定データを読み取るサンプルのうち、割り込みを使用するリスト6のコートです。

+ 3 - 0
piodma_dht11/README.md

@@ -0,0 +1,3 @@
+# 第4章リスト8 
+
+PIOとDMAを組み合わせてDHT-11から測定データを読み取るサンプルです。dht11.pioはリスト6と変わりません。

+ 3 - 0
pwm_dma_sample/README.md

@@ -0,0 +1,3 @@
+# 第5章リスト4 pwm_dma_sample
+
+音声データは同梱していないので、適当な音声データを書籍を参考に作成してビルドしてください。

BIN
pwm_dma_sample/test12.raw


+ 5 - 0
pwm_sample/README.md

@@ -0,0 +1,5 @@
+# 第5章リスト2 LEDを脈動させるサンプル
+
+掲載時から若干の変更を加えています。
+
+主要な変更点は、`main()`関数内でsin()ルックアップテーブル `sine_lut`を用意しておき、割り込み処理内ではsin()を計算せず `sine_lut`を用いるようにした点です。一般論として割り込み処理は短時間で終了させるべきで、重い浮動小数点演算を割り込み処理内で行うのは適切ではありません。書籍ではわかりやすさ優先で浮動小数点演算を割り込み処理で行う形にしましたが、より実践的には、このサンプルのように事前計算しておいて割り込み処理を単純化すべきです。

+ 48 - 22
pwm_sample/pwm_sample.c

@@ -5,29 +5,42 @@
 #include <math.h>
 
 #define LED_PIN  PICO_DEFAULT_LED_PIN   // LED
-#define PWM_SUB_PIN 2                   // サブ用
+#define PWM_SUB_PIN 2                   // サブ用 (GPIO2)
 
-#define PWM_FREQ    1000
-#define PWM_DIVIDER 100.0f
+// PWM信号のクロック分周比
+#define PWM_CLOCK_DIVIDER 100.0f
+// LEDの明るさを更新する周波数 (Hz)
+// この周波数でPWM割り込みが発生する
+#define EFFECT_UPDATE_FREQ_HZ 1000
+// サイン波ルックアップテーブルのサイズ
+#define SINE_LUT_SIZE 256
 
-#define PI 3.14159265358979323846
-
-uint slice_num = 0;
-uint sub_slice = 0;
-uint16_t wrap = 0;
+static uint slice_num = 0;
+static uint sub_slice = 0;
+static uint16_t wrap = 0;
+// サイン波の値を格納するルックアップテーブル
+static uint16_t sine_lut[SINE_LUT_SIZE];
 
 void pwm_irq_handler(void)
 {
-    static uint counter = 0;
-
+    // 割り込みフラグをクリア
     pwm_clear_irq(slice_num);
-    if(counter++ >= PWM_FREQ) {
-        counter = 0;
+
+    // 0.5Hzの呼吸エフェクトを実現するためのカウンタ
+    // (EFFECT_UPDATE_FREQ_HZ回で1周期 = 1秒)
+    static uint effect_counter = 0;
+
+    // カウンタをルックアップテーブルのインデックスにマッピング
+    uint lut_index = (effect_counter * SINE_LUT_SIZE) / EFFECT_UPDATE_FREQ_HZ;
+    uint16_t level = sine_lut[lut_index];
+
+    // 計算済みの値をPWMデューティ比として設定
+    pwm_set_gpio_level(LED_PIN, level);
+    pwm_set_gpio_level(PWM_SUB_PIN, level);
+
+    if(++effect_counter >= EFFECT_UPDATE_FREQ_HZ) {
+        effect_counter = 0;
     }
-    double amp = fabs(sin(PI * ((double)counter/(double)PWM_FREQ)));
-    uint16_t cc = (uint16_t)((double)wrap * amp);
-    pwm_set_gpio_level(LED_PIN, cc);
-    pwm_set_gpio_level(PWM_SUB_PIN, cc);
 }
 
 int main()
@@ -38,10 +51,20 @@ int main()
     gpio_set_function(PWM_SUB_PIN,GPIO_FUNC_PWM );
 
     pwm_config c = pwm_get_default_config();
-    pwm_config_set_clkdiv(&c, PWM_DIVIDER);
-    wrap = (clock_get_hz(clk_sys) / PWM_DIVIDER) / PWM_FREQ;
+    // クロック分周を設定
+    pwm_config_set_clkdiv(&c, PWM_CLOCK_DIVIDER);
+    // PWMのWRAP値(カウンタの最大値)を計算・設定
+    // これにより、PWM割り込みが EFFECT_UPDATE_FREQ_HZ で発生するようになる
+    wrap = (uint16_t)((clock_get_hz(clk_sys) / PWM_CLOCK_DIVIDER) / EFFECT_UPDATE_FREQ_HZ);
     pwm_config_set_wrap(&c, wrap);
 
+    // プログラム開始時にサイン波のルックアップテーブルを生成
+    for (int i = 0; i < SINE_LUT_SIZE; ++i) {
+        // 0からPIまでの半周期のサイン波を計算
+        double sin_val = sin((double)i * M_PI / (double)SINE_LUT_SIZE);
+        // 計算結果をPWMのデューティ比にスケーリングしてテーブルに格納
+        sine_lut[i] = (uint16_t)((double)wrap * sin_val);
+    }
     // LEDのスライスを設定
     slice_num = pwm_gpio_to_slice_num(LED_PIN);
     pwm_init(slice_num, &c, false);
@@ -49,16 +72,19 @@ int main()
     sub_slice = pwm_gpio_to_slice_num(PWM_SUB_PIN);
     pwm_init(sub_slice, &c, false);
 
-    // 念のためにIRQをクリアしておく
+    // 割り込み設定
+    // 割り込みハンドラは片方のスライス(slice_num)にのみ設定すれば、
+    // 両方のGPIOを同時に制御できる
     pwm_clear_irq(slice_num);
-    //
     pwm_set_irq_enabled(slice_num, true);
-    irq_set_exclusive_handler(PWM_DEFAULT_IRQ_NUM(), pwm_irq_handler);
-    irq_set_enabled(PWM_DEFAULT_IRQ_NUM(), true);
+    irq_set_exclusive_handler(PWM_IRQ_WRAP, pwm_irq_handler);
+    irq_set_enabled(PWM_IRQ_WRAP, true);
 
+    // 初期輝度を50%に設定
     pwm_set_gpio_level(LED_PIN, wrap/2);
     pwm_set_gpio_level(PWM_SUB_PIN, wrap/2);
 
+    // 両方のPWMスライスを同時に有効化
     pwm_set_enabled(slice_num, true);
     pwm_set_enabled(sub_slice, true);
 

+ 7 - 0
spi_sample/README.md

@@ -0,0 +1,7 @@
+# 第8章リスト1とリスト3
+
+ST7789に画像を表示するリスト1およびDMAバージョンのリスト3をあわせたソースです。使用したST7789ディスプレイモジュールは240×240ドットの解像度です。異なる解像度のモジュールを利用する方は、書籍の記述とデータシートを参考にしてソースを書き換えてください。
+
+なお、ST7789を採用したLCDモジュールであっても同じ初期化コードで動作するとは限らないことに注意してください。本ソースはSPI MODE 3で動作することを前提にspi_set_format()のパラメータを設定していますが、そうではないモジュールもあるかもしれません。
+
+また、表示する画像は、rgb565.pyを使ってRGB565のビットマップに変換して利用してください。

+ 3 - 0
ssd1306_image/README.md

@@ -0,0 +1,3 @@
+# 第7章リスト2、リスト4
+
+I2C接続OLEDモジュールSSD1306にビットマップイメージを転送するサンプルです。DMAを使用しないリスト2と、DMAを使用するリスト4をあわせたソースになっています。128×32ドットのモノクロ2値画像を用意して、img_to_bin.pyでSSD1306のGDRAM形式に変換して試してください。

+ 1 - 0
ssd1306_test/README.md

@@ -0,0 +1 @@
+# 第7章リスト1 I2C接続確認

+ 5 - 0
usb_hid_sample/README.md

@@ -0,0 +1,5 @@
+# 第9章リスト2
+
+AとBを入力でき、Caps Lockインジケータの機能を備えた簡単キーボードのサンプルです。GP16とGP17にタクトスイッチを接続し、双方のタクトスイッチの一方をGNDに落として試してください。
+
+なお、Caps LockインジケータはPicoまたはPico 2のオンボードLEDを使用しています。Pico W、Pico 2 Wでは書き換えが必要になります。