Raspberry Pi Pico windows環境でさっとLチカする

#目標
さっとbuildしてuf2形式作ってD&DしてGPIO25のLEDをLチカさせたい。
Register仕様書とリファレンスコードさえあれば良い、という方はやってみてください。
あとSDK documentみてちゃんとやるほうが良いです。

あと、この記事は自分のpico-sdkの理解メモです。

##経緯
Pico、安いのは良いんだけど環境作る時に割と大きめのpico-sdkの中身を一度cmakeで良い感じにしないといけないのが辛かったので、コンパイラさえあればあとは仕様書とにらめっこして自分でsdkの好きなところつまんで使えると嬉しいね(自分が)という観点。

##準備物とか作業前提の環境とか
- Windows 10 64bitとcmd.exe
- Raspberry Pi Pico1枚
- Windowsで動くgit環境 : https://git-scm.com/download/win
- Visual Studioでもなんでもいいので.exe作れる環境(mingwでもいい)

- GNU Arm Embedded Toolchain Downloads
https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
ここのwindows用の32bit版をdownloadしてきて展開してbinにpath通しておきます。

- 本家raspberrypi pico sdk github

https://github.com/raspberrypi/pico-sdk

cloneして適当に展開しておきます。

##elf2uf2.exeを作る
elf2uf2は公式の設計だとelfからelfheaderを読んでuf2形式にするコンバータです。
sdkをcloneないしunzipすると以下のmain.cppがあるのでそれをmakeしてexeにします。
https://github.com/raspberrypi/pico-sdk/tree/master/tools/elf2uf2/main.cpp

uf2.hが無いとか言われるけど以下にあるのでpathを通してmakeしちゃいます
https://github.com/raspberrypi/pico-sdk/tree/master/src/common/boot_uf2/include/boot

pico-sdkが作業フォルダ配下にあるとして以下の程度で良いはず。

```bat
cl main.cpp /Ipico-sdk\src\common\boot_uf2\include\ /Feelf2uf2 /nologo /Ox /GS-
```


なお、uf2形式は以下で仕様を参照できます。
https://github.com/microsoft/uf2


##Pico上でやろうとしていること整理
リセット -> Lチカ
本家のdocument参考にしつつ、raspberry pi pico今で回ってるやつはGPIO25番が表面実装LEDなのでそれを制御する。

```
0. IO_BANK0をreset : RESETS [1:5] IO_BANK0をresetしてRESET_DONEを調べてIO_BANK0がresetされるまで待つ
1. GPIO25設定1 : output enable -> disable
2. GPIO25設定2 : output -> disable
3. GPIO25設定3 : functionを5(SIO)に設定
4. GPIO25設定4 : output enable -> enable
5. GPIO25Lチカ : output -> enable -> ちょっとまつ -> output disable -> ちょっとまつ
6. 5.を繰り返す！
```

#コード
本当はSDK側のAPI群を使うのがフォーマルな気がする(好きなように)

start.s

```
.cpu cortex-m0plus
.thumb
    ldr r0, =0x20001000
    mov sp, r0
    bl main
    b .
```
stackアドレス設定してmainに飛ぶだけ。
もうちょっとちゃんとstack取った方が良いはず。



main側。

```c
#include <stdint.h>

//regs
#define REG_RW(x)                  ((x) + 0x0000)
#define REG_XOR(x)                 ((x) + 0x1000)
#define REG_SET(x)                 ((x) + 0x2000)
#define REG_CLR(x)                 ((x) + 0x3000)

#define GPIO_RW(x)                 ((x) + 0x0)
#define GPIO_SET(x)                ((x) + 0x4)
#define GPIO_CLR(x)                ((x) + 0x8)

#define RESETS_BASE                (0x4000C000)
#define RESETS_RESET               (RESETS_BASE + 0x0)
#define RESETS_WDSEL               (RESETS_BASE + 0x4)
#define RESETS_RESET_DONE          (RESETS_BASE + 0x8)
#define RESETS_RESET_IO_BANK0      (1 << 5)

#define SIO_BASE                   (0xD0000000)
#define SIO_GPIO_OUT               (SIO_BASE + 0x10)
#define SIO_GPIO_OE                (SIO_BASE + 0x20)

#define IO_BANK0_BASE              (0x40014000)
#define IO_BANK_GPIO_STATUS(num)   (IO_BANK0_BASE + (num * 0x4 * 2) + 0x0)
#define IO_BANK_GPIO_CTRL(num)     (IO_BANK0_BASE + (num * 0x4 * 2) + 0x4)

//gpio functions
#define GPIO_FUNC_XIP              (0)
#define GPIO_FUNC_SPI              (1)
#define GPIO_FUNC_UART             (2)
#define GPIO_FUNC_I2C              (3)
#define GPIO_FUNC_PWM              (4)
#define GPIO_FUNC_SIO              (5)
#define GPIO_FUNC_PIO0             (6)
#define GPIO_FUNC_PIO1             (7)
#define GPIO_FUNC_GPCK             (8)
#define GPIO_FUNC_USB              (9)
#define GPIO_FUNC_NULL             (0xf)
#define GPIO_LED_NUM               (25)

void
write32(volatile uint32_t addr, uint32_t value)
{
	*(volatile uint32_t *)addr = value;
}

uint32_t
read32(volatile uint32_t addr)
{
	return *(volatile uint32_t *)addr;
}

void
sleep_ms(volatile int a)
{
	while(a-- > 0);
}

void
reset()
{
	write32(REG_CLR(RESETS_RESET), RESETS_RESET_IO_BANK0);
	while((read32(RESETS_RESET_DONE) & RESETS_RESET_IO_BANK0) == 0)
	    sleep_ms(256);
}

void
setup_gpio()
{
	write32(GPIO_CLR(SIO_GPIO_OE), (1 << GPIO_LED_NUM));
	write32(GPIO_CLR(SIO_GPIO_OUT), (1 << GPIO_LED_NUM));
	write32(IO_BANK_GPIO_CTRL(GPIO_LED_NUM), GPIO_FUNC_SIO);
	write32(GPIO_SET(SIO_GPIO_OE), (1 << GPIO_LED_NUM));
}

int
main()
{
	reset();
	setup_gpio();
	while (1) {
		write32(GPIO_SET(SIO_GPIO_OUT), (1 << GPIO_LED_NUM));
		sleep_ms(0xFFFF);
		write32(GPIO_CLR(SIO_GPIO_OUT), (1 << GPIO_LED_NUM));
		sleep_ms(0xFFFF);
	}
	return 0;
}
```

90行程度。ほとんどレジスタの定義だったりなんですが、pico-sdk側ではcmakeと連携して動的に生成するheaderとかあって嫌だったのでべた書きしています。

makeするときのbatchは以下適当に。もういい加減cmakeとかvscodeとかでちゃんとやりたいんだけどね。

```bat
set EABIBINPATH=gcc-arm-none-eabi-10-2020-q4-major\bin
set MCPUOPTION=-mcpu=cortex-m0plus
set GCCOPTION=-O1 -fno-builtin -nostartfiles -nostdlib -mthumb -ffreestanding -c
%EABIBINPATH%\arm-none-eabi-as.exe %MCPUOPTION% start.s -o start.o
%EABIBINPATH%\arm-none-eabi-gcc %MCPUOPTION% test.c %GCCOPTION%
%EABIBINPATH%\arm-none-eabi-ld -T memorymap.ld start.o test.o
elf2uf2 a.out a.uf2

REM %EABIBINPATH%\arm-none-eabi-readelf -a a.out
REM %EABIBINPATH%\arm-none-eabi-objdump -S start.o
REM %EABIBINPATH%\arm-none-eabi-objdump -S test.o
REM
del *.out *.o
```

#Lチカする！
a.uf2をPicoを接続したストレージにD&Dすると、書いたプログラムが実行できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34428/9aa7a857-8434-1657-8463-8892db1f5e27.png)
こんな感じで光る！なんか写真がでかい！

##終わりに
windowsでmakeする環境結構めんどうじゃね？私だけかな。順当にSDK documentみてやった方が良いかもしれません。

##おまけ
前準備のgitの環境でpath通ってる環境ならcurlでコンパイラダウンロードしてpico-sdkをcloneしてpatch当ててを一息で行う環境を以下に作りました。
やってることはこの記事の寄せ集め。
https://github.com/kumaashi/raspberrypi-pico/tree/main/lchika

冒頭のgitをinstallしてcmd.exeからもろもろgitのbinpathが通っていれば、prepare_and_make.batを実行すればzipのダウンロードから環境作ってpatch当ててくれてa.uf2作ってくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34428/aa2d0a7d-0907-e296-1cf2-65f8165c1688.png)
こんな感じで。コンパイラのdownloadのURLはちょっと経つと古くなる可能性があるので適宜変更してくださいな。

##参考
全部思いっきり参考にしました。
https://github.com/dwelch67/raspberrypi-pico
https://qiita.com/yunkya2/items/5f0f5ebb0f26a52805d9
https://mickey-happygolucky.hatenablog.com/entry/2021/02/10/004921