1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お前らはまだmicropythonのuctypesを知らない

Posted at

はいすみません、イキりました。
知ってたら帰って貰ってよいですむしろお願いします。

ところで、pythonみたいなク〇言語を今の若い子が習うのは可哀そうだ!!
というのをtwitterで見ました。
python全然使えないのに思わずいいねしました。投げられた石はいくらでも受けます。

でもmicropythonは最高です!
全人類はmicropythonを使うべきだと思いました!!
いや、でも、おっさんハード系はC言語系好きですし必須なのは間違いないのですが、新人ハード系さんは普通のpython(とWSL)を覚える方が得だと思いますよ。

すみません、前振りはここまでにします。

環境

Raspberry pi picoです。
pico2も一緒だと思いますが、IO1.8VにしたRP2040基板作っちゃって移行できない…

micropythonのuctypes

公式ドキュメントのuctypesを見ても本家に飛ばされるだけでマジで意味わからんですよね。
C言語の構造体や共用体を無理やりbyte(array)に当てはめたものだと言えばいいですかね。
通常のstructと似てますが、micropythonライブラリのuctypes に書いてあるように、structは本来複雑なのは向かないです。
ちなみに被ってややこしいのですが、uctypesのstructとstructは別モノです。
うっかりstructだけで定義できるようにしてstruct.packとかやると?となってハマるので注意しましょう。

uctypesの良いところ

いろいろあるんですが、micropython的にはアドレスに直接書けるという点ですね。
ビットフィールドを判りやすくアクセスできるようになるのはネ申です。
個人的にはmem32で直接レジスタアクセスするならutypesを使う方が良いと思ってます。(mem32遅いですし…)

uctypesの例

とりあえずID以外のSPIのレジスタ定義したものをお見せします。
なお、皆さんにも苦しんでほしいですが、このまま書くと"import struct"と競合してエラーが出て?になるケースがあります。
解決方法は後述、はしますが、大したものではなく被らないようにするだけです。

from uctypes import BF_POS, BF_LEN, UINT32, BFUINT32, struct

# SPI0とSPI1のベースアドレス
_SPI_BASE_ADDR = (
    0x4003c000,
    0x40040000
)

# Control register 0
SPI_SSPCR0_FIELDS = {
    "SCR":      8<<BF_POS | 8<<BF_LEN | BFUINT32,
    "SPH":      7<<BF_POS | 1<<BF_LEN | BFUINT32,
    "SPO":      6<<BF_POS | 1<<BF_LEN | BFUINT32,
    "FRF":      4<<BF_POS | 2<<BF_LEN | BFUINT32,
    "DSS":      0<<BF_POS | 3<<BF_LEN | BFUINT32,
}

# Control register 1
SPI_SSPCR1_FIELDS = {
    "SOD":      3<<BF_POS | 1<<BF_LEN | BFUINT32,
    "MS":       2<<BF_POS | 1<<BF_LEN | BFUINT32,
    "SSE":      1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "LBM":      0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# Data register
SPI_SSPDR_FIELDS = {
    "DATA":     0<<BF_POS | 15<<BF_LEN | BFUINT32,
}

# Status register
SPI_SSPSR_FIELDS = {
    "BSY":      4<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RFF":      3<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RNE":      2<<BF_POS | 1<<BF_LEN | BFUINT32,
    "TNF":      1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "TFE":      0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# Clock prescale register
SPI_SSPCPSR_FIELDS = {
    "CPSDVSR":  0<<BF_POS | 8<<BF_LEN | BFUINT32,
}

# Interrupt mask set or clear register
SPI_SSPIMSC_FIELDS = {
    "TXIM":     3<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RXIM":     2<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RTIM":     1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RORIM":    0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# Raw interrupt status register
SPI_SSPRIS_FIELDS = {
    "TXRIS":    3<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RXRIS":    2<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RTRIS":    1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RORIS":    0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# Masked interrupt status register
SPI_SSPMIS_FIELDS = {
    "TXMIS":    3<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RXMIS":    2<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RTMIS":    1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RORMS":    0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# Interrupt clear register
SPI_SSPICR_FIELDS = {
    "RTIC":     1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RORIC":    0<<BF_POS | 1<<BF_LEN | BFUINT32,
}

# DMA control register
SPI_SSPDMACR_FIELDS = {
    "TXDMAE":   1<<BF_POS | 1<<BF_LEN | BFUINT32,
    "RXDMAE":   0<<BF_POS | 1<<BF_LEN | BFUINT32,
}


SPI_REGS = {
    "SSPCR0_REG":       0x000|UINT32,
    "SSPCR0":           (0x000, SPI_SSPCR0_FIELDS),
    "SSPCR1_REG":       0x004|UINT32,
    "SSPCR1":           (0x004, SPI_SSPCR1_FIELDS),
    "SSPDR_REG":        0x008|UINT32,
    "SSPDR":            (0x008, SPI_SSPDR_FIELDS),
    "SSPSR_REG":        0x00c|UINT32,
    "SSPSR":            (0x00c, SPI_SSPSR_FIELDS),
    "SSPCPSR_REG":      0x010|UINT32,
    "SSPCPSR":          (0x010, SPI_SSPCPSR_FIELDS),
    "SSPIMSC_REG":      0x014|UINT32,
    "SSPIMSC":          (0x014, SPI_SSPIMSC_FIELDS),
    "SSPRIS_REG":       0x018|UINT32,
    "SSPRIS":           (0x018, SPI_SSPRIS_FIELDS),
    "SSPMIS_REG":       0x01c|UINT32,
    "SSPMIS":           (0x01c, SPI_SSPMIS_FIELDS),
    "SSPICR_REG":       0x020|UINT32,
    "SSPICR":           (0x020, SPI_SSPICR_FIELDS),
    "SSPDMACR_REG":     0x024|UINT32,
    "SSPDMACR":         (0x024, SPI_SSPDMACR_FIELDS),
}

# SPIレジスタアクセス用。
SPI_DEVICE = [
    struct(_SPI_BASE_ADDR[0], SPI_REGS),
    struct(_SPI_BASE_ADDR[1], SPI_REGS)
]

例えばSPI0のSSPCR1レジスタのSSEビットとかは下記で設定できます。

SPI_DEVICE[0].SSPCR1.SSE = 0

32ビットレジスタとして読み書きしたい場合は*_REGを使います。

SPI_DEVICE[0].SSPDR_REG = 0x01

解説

共用体っぽいもの

例は下記。
辞書型でkeyにアクセス名を書くのはわかると思います。
valueは"|"で繋いでビットフィールドを記載します。

SPI_SSPCR0_FIELDS = {
    "SCR":      8<<BF_POS | 8<<BF_LEN | BFUINT32,
    "SPH":      7<<BF_POS | 1<<BF_LEN | BFUINT32,
    "SPO":      6<<BF_POS | 1<<BF_LEN | BFUINT32,
    "FRF":      4<<BF_POS | 2<<BF_LEN | BFUINT32,
    "DSS":      0<<BF_POS | 3<<BF_LEN | BFUINT32,
}

valueについては下記のとおり。
uctypesからimportするのを忘れないように。

BF_POS

ビットフィールドの開始位置を指定します。"<<"演算子で定義します。

BF_LEN

ビットフィールドのビット数を指定します。これも"<<"演算子で定義します。

BFUINT(8|16|32|64)

共用体にするビット数を定義します。
RP2040は32ビットレジスタなので、BFUINT32しか使わないかと思いますが。

構造体っぽいもの

例は下記。
辞書型でkeyにアクセス名を書くのは一緒です。
構造体は先頭からのオフセットをバイト数で記載しつつ、メンバがINT32とかUINT32を指定して"|"で繋ぎます。
オフセットがちょっと面倒ですが…
また、オフセットをメンバと同じにすれば、共用体アクセスも設定可能です。

SPI_REGS = {
    "SSPCR0_REG":       0x000|UINT32,
    "SSPCR0":           (0x000, SPI_SSPCR0_FIELDS),
    "SSPCR1_REG":       0x004|UINT32,
    "SSPCR1":           (0x004, SPI_SSPCR1_FIELDS),
    "SSPDR_REG":        0x008|UINT32,
    "SSPDR":            (0x008, SPI_SSPDR_FIELDS),
    "SSPSR_REG":        0x00c|UINT32,
    "SSPSR":            (0x00c, SPI_SSPSR_FIELDS),
    "SSPCPSR_REG":      0x010|UINT32,
    "SSPCPSR":          (0x010, SPI_SSPCPSR_FIELDS),
    "SSPIMSC_REG":      0x014|UINT32,
    "SSPIMSC":          (0x014, SPI_SSPIMSC_FIELDS),
    "SSPRIS_REG":       0x018|UINT32,
    "SSPRIS":           (0x018, SPI_SSPRIS_FIELDS),
    "SSPMIS_REG":       0x01c|UINT32,
    "SSPMIS":           (0x01c, SPI_SSPMIS_FIELDS),
    "SSPICR_REG":       0x020|UINT32,
    "SSPICR":           (0x020, SPI_SSPICR_FIELDS),
    "SSPDMACR_REG":     0x024|UINT32,
    "SSPDMACR":         (0x024, SPI_SSPDMACR_FIELDS),
}

ちなみにオフセットは連続にする必要はないです。
不定値が入ってもよければ、飛ばして定義も可能です。
公式のuctypesの例を見てみてください。

structでアドレス割り当て

uctypesのstructに割り当てます。
レジスタアドレスを指定して直接アクセスできるようにします。

SPI_DEVICE = [
    struct(_SPI_BASE_ADDR[0], SPI_REGS),
    struct(_SPI_BASE_ADDR[1], SPI_REGS)
]

ところで、レジスタ設定にはAtomicアクセスがあります。
Atomicで設定したい場合は、素直にAtomicアクセス用のuctypes設定を作るのが良いと思います。
が、CLEARで1とかいう記述になるので混乱するよ!
例を記載します。
辞書型を使えば["XOR"]とか、それっぽく指定できますのでやりたい人は頑張って。

# Atomic register access offset
_MEM32_XOR      = 0x1000
_MEM32_SET      = 0x2000
_MEM32_CLEAR    = 0x3000

SPI_DEVICE_XOR = [
    struct(_SPI_BASE_ADDR[0] + _MEM32_XOR, SPI_REGS),
    struct(_SPI_BASE_ADDR[1] + _MEM32_XOR, SPI_REGS)
]

SPI_DEVICE_SET = [
    struct(_SPI_BASE_ADDR[0] + _MEM32_SET, SPI_REGS),
    struct(_SPI_BASE_ADDR[1] + _MEM32_SET, SPI_REGS)
]

SPI_DEVICE_CLEAR = [
    struct(_SPI_BASE_ADDR[0] + _MEM32_CLEAR, SPI_REGS),
    struct(_SPI_BASE_ADDR[1] + _MEM32_CLEAR, SPI_REGS)
]

structとuctypesのstruct

いうほど書くまでもないです。
両方使う場合はだいたいハマるので書いてます。
解決方法は単純に名前が同じとなって被らないようにするだけです。
どっちもイマイチなので公式で変えてほしい…

uctypesのまま使う

"import uctypes"にして、"uctypes.struct"にします。
"BFUNIT32"とかも"uctypes.BFUINT32"になるのは面倒ですが。

structを別名にする

"import struct as st"とかですね。
"st.pack"とかにすれば被らないというだけです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?