いろいろ試す59

●STM32L1とSTM8シリーズがNRNDになってしまった…

ほら言わんこっちゃない…温めすぎて腐ってしまいました…orz
STM8シリーズはもう少し進めたかったのですがSTM32C0が登場して
既存の8ビットはエコシステムから排除され駆逐される勢いとなって
しまいましたね…STM32L1のほうはSTM32L0,STM32U5とかが登場した
のでお役御免となった感じでしょうか…
今のラインナップを鑑みるにねむいさんが使ってきたSTM32F2やF4,
L4,L5とかも次のNRND候補になっちゃいそうですね…
まぁこれも時代の流れということで…とにも書くにもSTM8Lに関しては
最後っ屁で一件記事書いてクローズとさせてもらおうとおもいます…!


●STM32F4のHALじゃなくてSPLがアップデートしていたが…

2022年にHALライブラリじゃなくて古のStandardPeripheralLibraryが
アップデートしていたようです
…内容は…


余計なことするな〜〜!
GPIOのレジスタ周りは致命的だったのでここだけ元に戻してアップデート
いたしました…


●ILI9341をシリアル接続で動かした際にDeviceIDを読み出す方法
10年前に一度記事を書きましたが10年の時を超えて皆忘れてしまってる
ようなので改めて詳しく解説しようと思います。

ILI9340,ILI9341,ILI9342のILITEK製のTFT-LCDコントローラはDeviceID
をそれぞれ持っておりそれらの読み出しコマンドは"0xD3"を使用します。


尤もi8080などのパラレルなデータバスの読出しはデータシートの
通りに従えば期待していたDeviceID(ILI9341なら0x9341)を読み出す
ことができます。しかし、SPI(以下シリアル)バス接続の場合は何故か
この通りにやっても絶対に読み出すことはできません。

これについては10年以上前から様々なコミュニティで話題が上がって
おりますがシリアル接続では独特の手順をもってしないと読みだす
ことができません。

ILI934x系のコントローラはシリアル接続でレジスタの値を読み出す
ためにはデータシート未記載の"undocumented"なレジスタにアクセス
する必要があります。それがRegisterReadIndex(0xD9)です!

使い方は以下の通りです。ReadID4コマンド(0xD3)を実行してシリアル
接続でDeviceIDを読み出す手順です。

/**************************************************************************/
/*!
Read ID ILI934x.
*/
/**************************************************************************/
static uint16_t ILI934x_rd_id(uint8_t cmd)
{
uint16_t val;
uint16_t temp;

ILI934x_wr_cmd(0xD9); /* SPI Register Read Command */
ILI934x_wr_dat(0x10); /* Read Mode Enable,1st Byte */
temp = ILI934x_rd_cmd(cmd); /* Dummy Read */

ILI934x_wr_cmd(0xD9); /* SPI Register Read Command */
ILI934x_wr_dat(0x11); /* Read Mode Enable,2nd Byte */
temp = ILI934x_rd_cmd(cmd); /* Dummy Read */

ILI934x_wr_cmd(0xD9); /* SPI Register Read Command */
ILI934x_wr_dat(0x12); /* Read Mode Enable,3rd Byte */
temp = ILI934x_rd_cmd(cmd); /* Upper Read */

ILI934x_wr_cmd(0xD9); /* SPI Register Read Command */
ILI934x_wr_dat(0x13); /* Read Mode Enable,4th Byte */
val = ILI934x_rd_cmd(cmd); /* Lower Read */

val &= 0x00FF;
val |= (uint16_t)temp<<8;

return val;
}
☝こんな感じでインデックスを一つずつ指定して読みだしていきます。
 このようなかんじでシリアル接続でもDeviceIDを読みだせます。


なんでデータシート未記載なんだYO!とお思いの方もいらっしゃる
でしょうが、実は近年にリニューアルされたデータシートにはちゃんと
記載されてやがったりします。これはILI9342"C"のデータシートです。
もちろんILI9342無印では記述がありませんでした。


また、同様のアクセス方法としてはILI9486Lも同機能のコマンドが存在
しております。てか"DGAMCTRL (Digital Gamma Control 2)"とかなってて
でたらめでめっちゃ適当ですね…


さらに今年判明した衝撃の事実ですが128x128サイズを主にサポートする
ILI9163系のコントローラも0xD9を実行すると不可能だと思われていた
シリアル接続のDeviceIDを読み出し可能なことを確認しました!!
ソースコードに上記のように反映しております!

という感じで定期的に持ち上がるILI934x系のネタですがシリアル接続で
この液晶を使うシーンが極めて多いので情報としてしっかり残して
おこうと思います!
最も昨今はST7789Vx一族にだいぶ押されておりますがー!

これらの成果物は主におきぱにあるSTM32F4のFatFs移植作例に収蔵
されております。プロジェクトの下記ドライバを参照してください。
./lib/display/drivers/src/ili934x.c(ILI9342を除く)
./lib/display/drivers/src/ili9342.c
./lib/display/drivers/src/ili9481.c
./lib/display/drivers/src/ili9163x.c

●新液晶モジュール続々登場!
液晶モジュールつながりでもう一件です。


超久しぶりですがこの前ST7789V2にも対応したついでに新たに買った
中華TFT液晶モジュールをいくつか紹介させていただきます。


こちらはなななんと5inchで540x960pixelでi8080データバス対応という
今までで最大サイズの液晶モジュールです!!!!!
コントローラはNT35516という品種です。
480x854が今までの最大サイズだったので大進化ですね〜


で、動かそうとすると結構癖があった…
データバスが外来ノイズに弱くてすぐ崩れるのでねむいさんのいつもの
に合わせるのに結構苦労しましたがなんとか安定動作できるように
なりました。それにしても540x960は広すぎる…


ついに(叢雲のコスプレをした)いないさんの全身が見切れせずに
表現できるように!!やった!!

とはいえ静電性タッチパネルが装着されたモデルがまだAliexpressに
登場してないのでメインで使用する段階ではないです…

やっぱしILI9806Gなこいつが一番使いやすいですね…!



お次はHX8363Bをコントローラに持つ3.97inch,480x800な現在では
定番サイズの液晶です。こちらもタッチパネルなしです。
ていうかなんなんだ真ん中のシールは。

このコントローラはちょっと癖があって8/16bitのインターフェース
を決定する設定は外部に出るピンではなくレジスタに入れる値で決定
します。これ10年以上前に使用したS1D19122に似てますね〜


動かしたところです。
まぁ可もなく不可もなくといったところですね〜

やっぱしILI9806Gなこいつが一番使いや(r



こちらはR61408が使用された3.97inch,480x800のものです。
FT5216の容量性タッチパネルコントローラがついたタッチパネル
つきになっております☆


最近に販売されている中華液晶の下駄基板はパタンでお絵描きしたりが
なくなって殺風景なのでちょっと寂しいですね…一応RGB/8bit/16bit
そしてI2Cまで使用可能です。R61408のシールは私が貼りました。


動かしたところ。
かなり発色が良いですね〜

でもやっぱしILI9806Gなこい(ry




んでもって10年くらいまえのりべんぢですがNT35510のまともな奴が
手に入りましたので動かしてみました。
サイズは定番の3.97inch,480x800です。


こちらも発色がなかなかでした…以前はまともに表示できなかったので
10年越しにりべんぢできたかんじですね…!



最後にRM68180が使用された3.97inch,480x800のモジュールです。
FPCコネクタ接続でインターフェースは8bitという速度的に苦しい
ものですがはてさて


かなり苦労しましたが何とか表示できるようになりました。
RM68120のコードとほぼ同じなのでrm68120.cと同居しました。




というわけで最近久しぶりに入手したTFT液晶モジュールを一気に
紹介しましたが現時点では使いやすさ、安定性、表示領域の面を
鑑みるとILI9806Gなこいつが鉄板と言わざるを得ませんね。

NT35516のやつがもうちょっと頑張ってくれたらよいのですが、
これから先も当分の間はILI9806G使ったやつのお世話になりそうです。

STM32L5を使ってみる3 -OCTO-SPIでQUAD-SPIROMを使おう-

●QUAD-SPI接続NORFLASH-ROMを使いこなせ!
FONTX2ファイルなどの大容量のデータはSTM32等の大規模マイコンと
いえどもフラッシュ容量を非常にひっ迫します。
以前からはそういったデータは外付けのQSPI-ROMに格納し、そこから
アクセスするといった手法が定番となっております。

STM32L5では信号線を最大8本持つクロックドシリアルインターフェース
いわゆる"OCTO-SPI"をもちさらに利便性が高まっております!
このOCTO-SPIを使ってQSPI-ROMと接続し内蔵フラッシュメモリのように
使えるMemoryMappedModeで読み出していこうと思います!


●もはや定番☆WinbondのW25Q128JVSIQ

前々回からの紹介の通り、ねむいさんは入手も容易で十分な容量(16MB)、
高速なクロック(133MHz)でデフォルトでQUAD-SPIモードになっていて
コマンドが省略出来て扱いやすいW25Q128JVSIQを使用しました。

W25Qのシリーズにはいろいろありますが末尾"IQ"のものは上でも
書いた通り最初っからQUAD-SPIモードになっており今から紹介する
メモリマップドモードで投げるコマンドがいくつか省略出来て楽です。
digikeyやmouserで購入の際はうっかり違うやつを買わないように
型番には注意してください。


●OCTO-SPIをMemoryMappedModeで使用するためのコード手順
STM32L5に搭載されているOCTO-SPIはSTM32F7などにあったQUAD-SPI
インターフェースからされに拡張され、読み出しのほかに書き込みも
サポートされています。基本的な設定の流れはQUAD-SPIの頃とほぼ
同じですがOCTO-SPIならではの設定もあるので注意しましょう。

↓まずはI/O設定です。

/**************************************************************************/
/*!
@brief OCTO-SPI GPIO Configuration.
@param None.
@retval None.
*/
/**************************************************************************/
void OSPI_IoInit_If(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

/* Initializes the peripherals clock */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_OSPI;
PeriphClkInit.OspiClockSelection = RCC_OSPICLKSOURCE_SYSCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
for(;;);
}

/* Peripheral clock enable */
__HAL_RCC_OSPI1_CLK_ENABLE();

/* Alternate GPIO enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

/* PE12 as OSPI_IO0 */
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

/* PB0 as OSPI_IO1 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/* PE14 as OSPI_IO2 */
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

/* PE15 as OSPI_IO3 */
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

/* PB10 as OSPI_CLK */
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

/* PA2 as OSPI_NCS */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Alternate = GPIO_AF10_OCTOSPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

これはNUCLEO-L552ZE-Qから出たQUAD-SPIの足に対応したものです。
OCTO-SPIなのに8本全部使わぬぇのかと思いでしょうがこれはNUCLEO
が引き出してる足がそうなってるからそれに従ったわけなのでー

↓お次はOCTO-SPIの設定です。
/**************************************************************************/
/*!
@brief Configure OCTO-SPI as Memory Mapped Mode.
Winbond W25Q128JVSIQ specific setting.
* Address size is 24bit(16MBytes).
* Initially sets QUAD-MODE(not need quadmode command).
* MAX 133MHz CLK.
* Support "XIP",thus suitable for MemoryMappedMode.
@param None.
@retval None.
*/
/**************************************************************************/
void Set_OSPI_MemoryMappedMode(void)
{
OSPI_RegularCmdTypeDef sCommand = {0};
OSPI_MemoryMappedTypeDef sMemMappedCfg = {0};
uint8_t reg_data =0;

/* Initialize OCTO-SPI I/O */
OSPI_IoInit_If();

/* Initialize OCTO-SPI */
hospi.Instance = OCTOSPI1;
hospi.Init.FifoThreshold = 1;
hospi.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
hospi.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON;
hospi.Init.DeviceSize = 24; /* 128Mbit=16MByte=2^24 W25Q128JVSIQ */
hospi.Init.ChipSelectHighTime = 2; /* 2ClockCycle(18nSec@110MHz) Need for W25Q128JVSIQ >10nSec@read */
hospi.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
hospi.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
hospi.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
hospi.Init.ClockPrescaler = 2; /* 110MHzMAX/2 = 55MHz(MAX OSPI-CLK:90MHz) */
hospi.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_HALFCYCLE;
hospi.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
hospi.Init.ChipSelectBoundary = 0;
hospi.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
hospi.Init.Refresh = 0;
if (HAL_OSPI_Init(&hospi) != HAL_OK)
{
for(;;);
}

/* Enable Reset --------------------------- */
/* Common Commands */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
/* Instruction */
sCommand.Instruction = 0x66; /* Reset Enable W25Q128JVSIQ */
/* Address */
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.Address = 0;
/* Data */
sCommand.DataMode = HAL_OSPI_DATA_NONE;
sCommand.DummyCycles = 0;
sCommand.NbData = 0;

if (HAL_OSPI_Command(&hospi, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

/* Reset Device --------------------------- */
/* Common Commands */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
/* Instruction */
sCommand.Instruction = 0x99; /* Reset W25Q128JVSIQ */
/* Address */
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.Address = 0;
/* Data */
sCommand.DataMode = HAL_OSPI_DATA_NONE;
sCommand.DummyCycles = 0;
sCommand.NbData = 0;

if (HAL_OSPI_Command(&hospi, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

/* Enter Quad-SPI Mode --------------------------- */
/* Common Commands */
sCommand.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
/* Instruction */
sCommand.Instruction = 0x31; /* Set Status2 W25Q128JVSIQ */
/* Address */
sCommand.AddressMode = HAL_OSPI_ADDRESS_NONE;
sCommand.Address = 0;
/* Data */
sCommand.DataMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.DummyCycles = 0;
sCommand.NbData = 1;
reg_data = 0x02; /* Enable QuadI/O Mode */

if (HAL_OSPI_Command(&hospi, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

if (HAL_OSPI_Transmit(&hospi, &reg_data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

/* Enter MemoryMappedMode --------------------------- */
/* Read Commands */
sCommand.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES;
sCommand.AlternateBytes = 0xFF; /* Need for Fast Read QUAD W25Q128JVSIQ */
sCommand.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS;
sCommand.AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
/* Instruction */
sCommand.Instruction = 0xEB; /* Fast Read QUAD W25Q128JVSIQ */
/* Address */
sCommand.AddressMode = HAL_OSPI_ADDRESS_4_LINES;
sCommand.Address = 0;
/* Data */
sCommand.DataMode = HAL_OSPI_DATA_4_LINES;
sCommand.DummyCycles = 4; /* DUMMY 4Cycle for Fast Read QUAD W25Q128JVSIQ */
sCommand.NbData = 0;

if(HAL_OSPI_Command(&hospi, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

/* Write Commands */
sCommand.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;
sCommand.FlashId = HAL_OSPI_FLASH_ID_1;
sCommand.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
sCommand.AddressDtrMode = HAL_OSPI_ADDRESS_DTR_DISABLE;
sCommand.DataDtrMode = HAL_OSPI_DATA_DTR_DISABLE;
sCommand.DQSMode = HAL_OSPI_DQS_DISABLE;
sCommand.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
sCommand.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytes = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
sCommand.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
sCommand.InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
sCommand.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
/* Instruction */
sCommand.Instruction = 0x32; /* Page Write QUAD W25Q128JVSIQ */
/* Address */
sCommand.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
sCommand.Address = 0;
/* Data */
sCommand.DataMode = HAL_OSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.NbData = 0;

if(HAL_OSPI_Command(&hospi, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
for(;;);
}

/* Set OCTO-SPI as MemoryMappedMode */
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
sMemMappedCfg.TimeOutPeriod = 0;
if(HAL_OSPI_MemoryMapped(&hospi, &sMemMappedCfg) != HAL_OK)
{
for(;;);
}

}

流れとしてはリセット有効->リセット->クアッドI/Oモード有効->
クアッド高速読み出しコマンド->クアッドセクタ書き込みコマンド
->HALのMemoryMappedMode移行関数呼び出し
となっております。

"クアッドI/Oモード有効"はW25Q128JVSIQ使うなら本来不要ですが
"念のため"突っ込んどきました…一応なくても動作します。

注意点ですがOCTO-SPIは書き込みもMemoryMappedModeが使用可能の為、
クアッド読み出しコマンドの次にセクタ書き込みコマンドの設定が
必須になります!!


なぜそうなっているかというとHALの関数内で書き込みコマンドの
設定まで要求してきやがるからです(別に今回書き込み要らんのに☠)
これも設定しないと先に進めません!

●動作してるところ

MemoryMappedModeで設定したアドレスはSTM32F7のでやった時と同じく
0x90000000からとなりますので過去のリソースが利用できます。
ねむいさんの使い方ではFONTX2ファイルのAnkの方を0x90000000、
Sjis-Kanjiフォントの方は0x90010000から参照しております。
Ankのファイルサイズはどんなにでかくとも65536byte以上にならない
だろうという打算でこのオフセット値を設定してます。


こんな感じでFONTX2(全角&半角)ファイルを仕込んだW25Q128JVSIQから
内蔵フラッシュと変わらない感覚でデータを読み出し表示ができました。
使用したフォントは小夏フォントを12ptのFONTX2に変換したものです。
STM32L5のOCTO-SPIは命令キャッシュだけでデータキャッシュは無い
ですが文字表示や操作感に特にもたついた感じはしてませんね。



デバッガで0x90000000番地を表示してみました。
MemoyMappedMode有効後は内蔵フラッシュと同じようにデバッガで
内部のデータを参照できてますね。



●ところでどうやってQSPI-ROMにデータ書くの?
…今回の記事はこれが最大の難関です…!
OpenOCDではDiscovery系のボードではI/Fの足もQSPI-ROMの品種も
決め打ちのためにQSPI-ROMの読み書きがサポートされていますが
NUCLEO系のやつは足は出ているもののその先に何がつながるかは
わからないのでサポートしているものは何にもありません。

したがってOpenOCDのmmwのコマンドでそれぞれのROMデバイス向けに
上で書いたコードを16進数並べてがむばって定義していくしかないの
ですがねむいさんはそんなことやる気力がないので…
Jtagkey2とSPI-ROM書き込みプログラム"Flashom"にてFONTX2データを
書き込むことにしました。

使用するフォントは12x12pxの小夏フォントです。
AnkはKONATHN12.FNTを、S-JisのKanjiはKONATZN12.FNTを使用します。
上記ファイルについて、私のL5プロジェクトの下記ディレクトリを
参照してください。
./lib/FONTX2/inc/fonts/KONATSU


※小夏フォントについてはTTFファイルで提供されております。
今回はあらかじめFONTX2コンバータでTTF->FONTX2形式に変換
して全角/半角とも使用しております。



まずはSPI-ROMの認識と読み出し。ちゃんと認識されているのを確認。
ついでに読み出したデータは後で使用するのでbin形式で保存します。



JtagKey2(で使用されているFT2232H)のSCKは最大30MHzのスピートなので
配線長はなるべく短くしましょう。5cm以下だとまず問題ないです。
残念ながらげたゲタ基板とNUCLEO基板を合体した状態ではまともに認識
出来なかったのでNUCLEOから5Vだけ拝借してW25Q128JVSIQと一対一で配線
する形になります。なお、CSはプルアップ必須なのですがねむいさんは
念のためCLK除くすべてのI/Oを22kohmでプルアップしてます。
一方CLKはSDMMCと同じく直列終端抵抗を忘れずに!!
ねむいさんはSDMMCと同じ33ohmを直列終端として使用してます。

お次は書き込み用バイナリの作成。
ねむいさんの知る限りではFlashromはアドレスをオフセット指定して
一部の領域だけを書き込むことができず、全領域一気に書き込みしないと
いけないので先に読み出したbinファイルを利用してそこにFONTX2ファイル
を上書きする形で配置しFONTX2を仕込んだROMファイルを作成します。
バイナリ編集プログラム"Stirling"を駆使して0x0000000にANKフォントを
0x00010000にKanjiフォントをそれぞれ上書きモードでコピペ配置します。
オフセットは1バイトでもずれないように注意してください。


Ankは配置されるとこんな感じ


Kanjiはこんなかんじになります。


Stirlingのビットマップ表示だとバイナリイメージはこんな感じです。
まだまだ一杯データ詰め込めますね。


そして書き込み。JtagKey2とFlashromを使用した際は通常のSPIで
書き込むことになりますがW25Q128JVSIQはセクタ書き込み対応なので
16MByteもある容量でも2分もかからず終わっちゃいます。


ねむいさんの公開しているプログラムでは一応作成済みのQSPI-ROM
イメージ(128QVFONT.bin)を添付して簡単な使用手順も書いてますので
酔狂な方はご参考に…。

まぁ私のぶろぐ普段から見られてる方は楽勝だと思います。





そんなわけでSTM32H5からやる予定だったOCTO-SPIでしたがL5でもうまく
使いこなすことができました!
L5でやりたいことは一通り全部やり切ったのでいい感じでSTM32H5へと
繋げて行きたいとおもいます!!

STM32L5を使ってみる2 -FatFsその他の実装-

●いつものやつを実装しよう
前回は144Pin版NUCLEO基板の下駄基板のビジュアルだけ紹介しましたが、
今回はそれを動かすためのファームウエアの要所を紹介していこうと思います。
ねむいさんの"いつもの"ことChaN師のFatFs実装を中心としております。

以下、STM32L5-NUCLEOのFatFs実装例のプロジェクトの要点を掻い摘んで
解説していきたいと思います。

●GCCビルドのためのCortex-M33向けオプション
先ずはmakefileを見ていただいたらお分かりと思いますがCortex-M33は
基本的にCortex-M4Fの進化版と思って頂けたらよいと思います。
GCCコンパイラに与えるCortex-M33コア向けオプションは以下になります。

-mcpu=cortex-m33 -mtune=cortex-m33 -mfix-cmse-cve-2021-35465

-mfix-cmse-cve-2021-35465とかいうのはArmv8-Mコアの脆弱性対策です。
2023年現在の最新のGCCコンパイラはデフォルトでONになっておりいちいち
入れる必要はないかもしれませんが念のため明示してます。




●基本の"き"、タイマーとUARTの実装
STM32に代表されるCortex-M系のコアはほぼすべてにSystickタイマーが
標準装備されております。こちらを1mSecウエイト用のタイマーとして、一方
マイコン周辺機器のタイマー機能を利用してuSecオーダーのタイマーを実装します。
今回はuSec用にはTIM5を利用しました。

以下systick.cより抜粋
	/* Making MicroSecond-Order Timer uses general purpose timer! */
/* Enable timer clock */
USEC_TIMx_CLKEN();

/* calculate TIMx(2 or 5) Prescaler clock(APB1) */
if(RCC->CFGR & RCC_CFGR_PPRE1){
if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV2){
cal_usec_divide = SystemCoreClock/2; /* (HCLK(=SYSCLK)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV4){
cal_usec_divide = SystemCoreClock/4; /* (HCLK(=SYSCLK)*4)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV8){
cal_usec_divide = SystemCoreClock/8; /* (HCLK(=SYSCLK)*8)*2 */
}
else if((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_HCLK_DIV16){
cal_usec_divide = SystemCoreClock/16; /* (HCLK(=SYSCLK)*16)*2 */
}
}

/* usec wait timer settings */
TimHandle.Instance = USEC_TIMx;
TimHandle.Init.Period = UINT32_MAX;
TimHandle.Init.Prescaler = ((cal_usec_divide)/USEC_INTERVAL) - 1;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
/* Capture error */
while (1);
}

USEC_TIMx->CR1 &= ~(TIM_CR1_UIFREMAP); /* Disable UIF Remap(Must Need!) */
HAL_TIM_Base_Start(&TimHandle);

最近のHALライブラリは"AutoReloadPreload"構造体が追加されているので
移植の際はご注意ください。また,UIFRemapビットにもご注意ください。


UARTについてはNUCLEO-L552ZE-Q基板上ではSTLinkのVCPとはLPUART
なるもので接続されております。一般のUARTとは多少違いがありますが
やることは同じです。LPUART1、GPIOG7,8をTX,RXとします。
一応LPUART以外のポートも使用できるようにしておりますので詳細は
uart.cupport.cを参照願います(丸投げ)
っとその前にLPUART1を使う際はLPUART1へのクロック供給のほかに
"HAL_PWREx_EnableVddIO2()"の実行も忘れないようにしてください。

またリングバッファによるノンブロッキング送受信なのはいつも通り
なのでスムーズにデータのやり取りができると思います。


●SDMMCドライバの実装・FatFsとの結合
STM32L5にはSDMMCがありますのでこれを実装しますが数年前STM32H7に
実装したSDMMCのドライバ
をスライド移植するだけのお手軽です☆
ぶっちゃけ今回の移植作業で一番楽でした。


一応使用上の注意なのですがSTM32H7と同じくSTM32L5のSDMMCはDDR
モードが使用可能ですがSDMMCに供給するクロックがバイパスされて
いないと使用不可となりますのでご注意ください。

ねむいさんの作例ではデフォルトではHSI48をSDMMCにバイパスなしで
供給しているためDDRで使用することができませんorz


もしDDR対応のeMMCをお持ちの方はコアクロックを110MHzから100MHzで
動かすとHSI48を使わずコアクロックをSDMMCのクロックにするよう
切り替えますので試してみてください。

STM32L5の最大動作クロックは110MHzなんですけどこういう制約が色々
あって100MHzで動かすほうがよっぽど有利なんですよね〜
100MHzとか昨今のマイコンでは遅い部類のクロックですがまぁ初代の
STM32F1とかのMAX72MHz動作と比べたら(消費電力も合間見ると)雲泥の
差ですがすっかり贅沢になってしまいましたね〜

STM32H7の実装でも実はひっそり最高速の480MHz対応してますが
こちらもいずれ紹介しようと思います。


●SPIとシリアル接続TFT-LCD

STM32L5のSPIモジュールも言ったって普通のSPIなので実装は容易
でした…が、DMAのほうはちょっと一癖ありました。
ねむいさんが知ってる従来のDMAではなくってDMAのチャネルがマルチ
プレクスされていて柔軟な設定ができるようになっており
、逆に扱いに
苦労しましたがExampleを参考に何とかSPIでDMAを吐けるようになって
おります。以下にDMAMUXの設定を抜粋しておきます。
全体は./lib/display/mcu_depend/display_if_basis.cを参照のこと。
#ifdef USE_DISPLAY_DMA_TRANSFER
/* DMA controller clock enable */
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();

/* Configure DMA request LcdDmaHandle */
LcdDmaHandle.Instance = SPILCD_DMA_CHANNEL;
LcdDmaHandle.Init.Request = SPILCD_DMA_REQEST;
LcdDmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH;
LcdDmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;
LcdDmaHandle.Init.MemInc = DMA_MINC_ENABLE;
LcdDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
LcdDmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
LcdDmaHandle.Init.Mode = DMA_NORMAL;
LcdDmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
if (HAL_DMA_Init(&LcdDmaHandle) != HAL_OK)
{
for(;;){
__NOP();
}
}
if (HAL_DMA_ConfigChannelAttributes(&LcdDmaHandle, DMA_CHANNEL_NPRIV) != HAL_OK)
{
for(;;){
__NOP();
}
}
__HAL_LINKDMA(&SpiHandle,hdmatx,LcdDmaHandle);

/* DMA interrupt init */
/* SPILCD_DMA_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SPILCD_DMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPILCD_DMA_IRQn);
#endif


また下駄基板ではST7789V2を使用したTFT-LCDモジュールを採用して
おりますがこちらのデータ線はSDIとSDOがマルチプレクスされたSDA
となっており、ST7789V2のデバイスIDを取得したいならばSTM32L5側で
SPIのMOSIとMISOを直結し読み出しの際にMISOの信号を正しく受け取る
MOSIをGPIOの入力にわざわざ切り替えてやる必要があります。
以下にそのコードを示します。
void Display_ChangeSDA_If(uint8_t sda_mode)
{
GPIO_InitTypeDef GPIO_InitStructure;

if(sda_mode == TFT_SDA_READ){
/* Enable CTRL Line GPIO Settings */
#if defined(USE_SOFTWARE_SPI)
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);

#else
/* Connect SPI MOSI pins to GPIO Input */
/* SPI MOSI pin configuration into GPIO input pullup */
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);
#endif
}

else {
/* Enable CTRL Line GPIO Settings */
#if defined(USE_SOFTWARE_SPI)
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Alternate = 0;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);

#else
/* Connect SPI pins to Alternate Function */
/* Restore SPI MOSI pin configuration */
DISPLAY_GPIOCLK_EN(DISPLAY_CLK_SDI);
DISPLAY_PERIF_CLK(ENABLE);
GPIO_InitStructure.Pin = CTRL_SDI;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStructure.Alternate = SRC_SDI;
HAL_GPIO_Init(DISPLAY_PORT_SDI, &GPIO_InitStructure);
#endif
}
}





ちなみにST7789V2側のドライバはデフォルトでは上記の切り替えの
必要なくIDを決め打ちで返すようにしてますのでもの好きな方だけ
SDAマルチプレクス有効にして試してみてください。
SDAマルチプレクスの際はSTM32側のMOSIとMISOをショートしてください。



ST7789V2からIDを読み出す設定でビルドしてデバッガで追っかけてみると
こんな感じにSDAからST7789V2のID(RDID2(0xDB)で0x85が返る)が
読まれてくるのがわかります。

SPI接続で動くモジュールはこんな感じでピン数節約のためにSDIとSDOが
マルチプレクスされてることもあるので覚えておいて損はないでしょう。



というわけでいつものChaN師のファイラー起動からの…

海で働いてるいないさんのpng形式のイラスツをlibpngでデコード!
(LCDのサイズ小さいので画像の一部しか表示できませんが)

libpngのほかにbmp形式はもちろんlibjpegやgiflibでjpg、gif形式
ファイルも表示できますがSTM32L5シリーズはフラッシュメモリ容量が
512kByteしかないのでフォントファイルを乗せつつ画像デコード用の
ライブラリ全部乗せは容量上非常に苦しいです。

そんなときのために容量を食うフォントファイルは外付けのSPI-ROM
にデータを配置してアクセスしたいところですが…
こういう用途に便利なOCTO-SPIという機能がありますが解説が長く
なりそうなので次回に続きます…!

大周回遅れでCortex-M33とSTM32L5はぢめました

●M33出てから何年たってるんだYO
禿ARMから小文字armに代わってもはや数年…ねむいさんもようやっと
ARMv8のCortex-M33コアに手を出しました…!


セキュリティー機能を強化したCortex-M33コアSTM32L5です!!
ていうか2023年現在はL5をそれぞれの分野別に進化させたSTM32U5や
STM32H5も登場しておりますがまずはL5から行きます!!


実際にはSTM32L552ZET6QUを使用したNUCLEO-L552ZE-Qボードを入手
いたしました。
このNUCLEO基板は(ねむいさんの中での)通称144Pin板NUCLEOと呼ばれる
ものです。NUCLEO基板族の中では最大の大きさとなりますがピン数の
多いマイコンを使っているおかげか外部へ引き出されたポートが固定化
されており、異なるCPU間でも下駄基板を使いまわせられるという
極めて大きな利点があります。


例えばSDMMCのポートはコネクタのこの位置に必ず配置されており、
一度基板をこしらえてしさえすればSTM32H7だろうがSTM32F4だろうが
STM32U5だろうがさらに最新のSTM32H5ですら144Pin版NUCLEOなら
同位置なわけでハードとソフトの切り分けがとても容易になります。


●てわけで144PinNUCLEO基板用汎用下駄基板を作ってみた。


ねむいさんは令和5年になっても自力でプリント基板起こせない雑魚
なのでユニバーサル基板とポリウレタン銅線でせこせこ配線です…。

大動脈の+3.3VラインはLCD,SDMMC,QSPI/OCTO-SPI,I2Sデバイスと大量に
電力を消費するのでNUCLEOの+3.3V出力から取得は不可能です。
したがってVbusの+5Vから+3.3Vを個別に作ることにしました。


ここで登場するのが廉価でぱわふりゃーなLDO、MIC37100-3.3WS-TRです!
LT1963Aが高嶺の花になり、ADP3338がNRNDになってしまった今、高性能
LDO難民となったねむいさんを救うぱわふりゃーなやつです。
ドロップアウト電圧は280mV/1A,10uF以上のセラミックコンデンサで
動作可能でお値段1個240円くらいなので非常にオトクです!!!
こちらでも紹介していますのでヨロシク!



ぇっと話がそれましたがSDMMCの端子はマイクロSDコネクタを採用
しました。それと以前から言っている通り各データラインのプルアップ&
終端の処理は有言実行でしっかり施しております。ねむいさんはCMDと
D0~D3のプルアップを22kohm,CLKの直列終端は33ohmとしました。


結構ラフな実装ですが、CLKに挿入する直列終端の効果は絶大です。
無抵抗状態ではこんな暴れまわっている生意気クロックラインも…


抵抗を直列にぶちこむだけでこんなにわからせられます!これで
安定してSDカードにアクセスができてソフトに専念できますね!


表示デバイスは3年前に購入して温めすぎて腐らせたTFT-LCDモジュール、
ATM0130B3を採用しました。240x240なのに1.3インチととっても小さい
モジュールなのですがこのNUCLEO基板にはまさに最適なヤツです。

このモジュールに使用されているコントローラはST7789V2です。V2とか
お尻に名前が付いていますが2023年現在、このST7789族は電子工作の
お供として必ず名前が上がるくらい有名で、もはやILI9341よりも広く
使用されるようになり、まさに覇権を握ったといってもよいでしょう。
マルチプレックス化されたデータラインからデバイスID取得する方法等の
使用時のコツは場を改めてソフト編で紹介します。


FONTX2等の大容量データの保存先としてメモリマップドでリニアに
アクセスできるQSPIを使用します。選んだデバイスはこれまた電子
工作で定番のSPI-ROMになったWinbondのW25Q128JVSIQです。
ちなみにW25Q128JV自身はQSPIモードをサポートしていますが
(パーツナンバー末尾のIQはデフォルトでQSPIモードの意味)、STM32L5に
おいてはOCTO-SPIというQSPIがさらにパワーアップしたモジュールが
搭載されております。
注:基実装はしましたがたぶんL5版で使用せずH5版Nucleoで使うと思います。
SAIについてもフラッシュ容量の関係でH5版Nucleoで紹介します。




そんなわけで下駄基板の紹介だけで一杯一杯になってしまいましたので
ねむいさんのいつものに相当するFatFsの実装等のソフトウエアの紹介は
次回以降順次紹介とさせていただきます。更新頑張るぞい!

また、気づいてる方もいると思いますがすでにおきぱにFatFsの実装例の
L5向けプロジェクト
を先行で公開しておりますので興味ある方はこれで
予習をお願いします。

GNSSモジュールを試用する20 -ublox SAM-M10Qはぢめました-

●入信


ついに…ねむいさんもublox教に入信しました!!!



ubloxのM10チップが使用されたSAM-M10Qです!!!!
そのSAM-M10Qが乗ったモジュールをAliexpressのMAKESYS社から
直接購入しました!


2013年から10年間使用し続けたGms-G9モジュールとのスペック
比較です。10年たつと隔世の感がしますね…特に消費電力の
低さが際立ってますね〜

ちなみに2023年現在かつてのみちびき一号機(PRN193)は引退して
電波は受信ができなくなり、Gms-g9に搭載されたMT3333チップは
現在では二号機、四号機のL1C/A信号しか受信ができず、みちびき
初号機後継機種のQZS1R(PRN196)やGEOと呼ばれる静止軌道Verの
みちびき(PRN199)の受信が不可能です。

そういう経緯もあり、このタイミングでublox教に入信した次第で
ございます。




さて、前途の通りねむいさんはSAM-M10Qが乗った出来合いの
ボードモジュールを購入したわけですが最低限のUARTの抵抗保護
や+5V入力を想定したレギュレータなどが搭載されております。


が、別に+5Vではなくとも"3V3端子"に3.3Vを直接ぶちかましても
全く問題なく使用が可能です(UARTの出力は+5V入力でもLVTTL)


●まずは動作比較してみよう


お次は実際の受信比較を行いました。場所は宇治川のほとりです!
AndroidのタブレットにSTM32 GNSSTr@ckerの仮想COMで接続してGms-g9,
SAM-M10Qモジュールと通信してどのみちびきが受信できるか比較
してみます。ついでにAndroidタブレットの内臓GPSモジュールも
比較しましょう。

今回の比較にあたり、内蔵GPSも仮想COM接続のGPSも使用できる
"GPS Connector"というアプリを使用しました。


まずはAndroidタブレットの内蔵GPSです。
御覧の通りPRN194,195,196が受信できてます。
196は初号機後継のQZS1Rです。Alldocubeっていう安物タブレット
でしたがなかなかやりますね。




お次はGms-g9です。STM32 GNSSTr@ckerの仮想COM機能で接続します。
Androidのアプリも内蔵と外付けと切り替えられるのが優秀です。


御覧の通り残念ながらPRN196の初号機後継機は受信ができませんでした。
MT3333はこれが限界か…!?


最後にSAM-M10Qです!!!


おおっ!衛星表記がちゃんと日本国旗の"QZSS"になってる!!!
2->PRM194,3->PRN195と読み替えてくださ…i…ん?
あれ…7ってPRN199のGEOですか・・・・雄々っ・・
本来なら4のPRM196も見えるはずですがここには映ってませんでした。


●さぁ実践DA!
そしてここからが本番です!!!111!!!
こら!!!そこ!!ページ閉じるな!!!!

ねむいさんの北陸侵攻はついに最終局面を迎え、目的の金沢兼六園
へと王手をかけておりましたが今回でついに兼六園に行きつきます!

ついでにSAM-M10Qを使ったロガーとCanmore製GP-102とのロギング
比較も行ったりします!!!



●2023.06.10 小松〜金沢

早朝の北陸小松IC…
ねむいさん東海自然歩道攻略依頼10年ぶりに高速バスを利用しました。


いざ行かん北陸道!


前田家ゆかりの小松天満宮です。


石川県の道路は赤い色が多いですが融雪装置の"さび"だそうです。


かつて駅だった寺井図書館です。


微妙に道に迷って手取川で北陸道に復帰して橋を渡ります。


だんだん日が昇ってきました…あづい


白山市ゆえに白山神社がやたら多いです


道の駅めぐみ白山で小休憩です。


国道にまとわりつくように北陸道が続きます。


松任駅近くの千代尼塚です。


怖い…


松任駅と謎のうんこです…
なんでうんこ…


再び国道北陸道です…
太陽光線が痛い…・


微妙にルートミスして大乗寺跡です。


北陸鉄道の終点野町駅です。
兼六園までもうすぐ!



昔ながらの風情があるにしの茶屋町です。


芭蕉の辻です!!うぉぉもうすこし!!


…ッッ北陸道のひとつの起点、香林坊に到着です…!!
ここから兼六園に向かいます。


金沢神社から兼六園に入ります…
なお、兼六園に来たのは30年ぶりです…




ぅおおおおおおおおおおお
懐かしすぎて全然覚えてない…
30年前来たときは真冬でしたしなおさらですね…


兼六園で一番有名なところです


兼六園は少し高台にあります。


カメラ構えても逃げるどころかポージングする鳥さん



兼六園を離れてお次は金沢城にむかいます。


本丸へ向かいます。


本丸は現在は自然の宝庫になっています。


櫓から金沢市街を見る


金沢譲を離れます。


人でごった返す近江町市場を抜けていきます


金沢駅手前の鼓門です…!
ついに…己の足で…敦賀から金沢までたどり着きました…
北陸新幹線が開通するよりもずっと先に…やったぞ!!!


そして本場の金沢ゴーゴーカレーも食す!!!


さらにさらに!
本計画の最終目的だった株主優待拳でグリーン車!!


そう、ここが北陸、金沢DA!


家族へのお土産もたくさん買い込んで超快適なサンダーバードの
しかもグリーン車で余裕の京都帰還をキメました…!カンペキダ!
というところで悲願の新幹線開通までに金沢到達をk…



おっとと肝心のGNSSのログの比較のほうですが距離が長すぎて
比較用のGPSロガーGP-102と重ねたらよくわからないので要所要所で
拡大していきます。


最初のスタート地点でコンビニによって朝飯を食べましたがGP102のログが
飛んでるのに対してSAM-M10Qはまとまってますね〜


香林坊から兼六園、金沢城周辺のログ比較です。
こちらもSAM-M10Qのほうがある程度まとまってますね…


さらに拡大です…本丸周辺(画像中央)の差を見ると歴然ですね…!
SAM-M10Qほんとすいごいってね



ところでSAM-M10Qはみちびきをどのレベルまで受信できていたのか!?
エクセル利用して衛星IDを抽出してみました。
NMEAver4.11は"$GQGSV"にセンテンスが変わってPRNも従来のものから
読み替える必要がありますが…!


初号機後継機も静止軌道版(GEO)みちびきもちゃんと補足しておりました!
やったね☆
ちなみに生ログはこちらになります。


というところでubloxのSAM-M10Qのすさまじい能力に感嘆としております!
Gms-g9が来た時も驚きましたが10年の歳月の進化はやはりすごかったです…!
そしてubloxは消費電流が多いという偏見も払拭されて私のGNSSロガーも
さらなるパワーアップを果たすことができました!!!!
これからもばりばり使いまくっていきますのでヨロシク!

いろいろ試す58

●ブログ画像の復活状況について
ねむいさんのブログではDropBoxを画像用サーバとして活用して
おりますが、さる2016年にDropBoxの仕様が変わって直りんが出来なく
なってしまい2012~2016年途中までの画像が見えなくなっておりますが、
悪いことに最初期の2009~2012年ごろのOneDriveの画像も表示できなく
なってしまいましてこちらの対応にも手を焼いております…

現在は2010年分まではOnedriveからDropBoxに移動して再び表示できる
ようにしておりますが膨大な作業量なので皆様首を長くしてお待ち
願います…。ああ先は長い…

●Picoscope7がアップデートされたが…
先月、Picoscopeのソフトウエアが6から7にメジャーバージョンアップ
されましたが、不安定で使い物になりませんでした。
今月に入って7.1.2にバージョンアップされて多少は安定したかな?
と思ったのですが…

I2Cなどのシリアルデコーディングをプリセットされたファイルを
ダブルクリックして起動したらすぐ固まってプログラムが落ちて
しまいました…orzダメじゃん!!
というわけで安定して使えるようになるまで6の最終版使いましょ


●STM32用I2Cタイミングレジスタ計算機アップデート
自分用に作ってきたSTM32向けI2Cレジスタ計算機ですが細かい修正を
加えて現在はGithub上で管理をすすめております


今回は固定値扱いしてきたTrise,Tfallを自由に設定できるようにしました。
デフォルト値は100,10としました。単位はnsecです。

計算機のコアの部分はSTM32U5のプロジェクトにあるI2C計算機をベース
としておりますが、nomalmode(100kHz)のTriseが640nsecに設定されていて
ちょっとおかしい(波形も)のでSTM32F3&STM32F0向け計算機の値を準拠と
しました。


うーむぴったりの値にならないばかりかちょっとクロック周波数が
どうしても遅めに出てしまう傾向がありますね…
なお、CubeMXも同じ傾向があるので細かいところを突き詰めたい方は
ツールでざっと算出して残りはオシロとにらめっこして突き合わせて
行くのがよいでしょう。っていい加減な私。


●STM32でI2C(マスター)が動…かねぇ!!!1!
I2Cバスは昨今の電子工作でセンサや表示デバイス等で多用されている
インターフェースの一つですが、STM32もI2Cをハードウエアレベルで
サポートしております。しかしSTM32でマスターとして使用する際には
下記の大きな注意点があります。

STM32はI2C基本的にスレーブデバイスとして動いている。
⊇藉化の手順を間違えるといきなりBUSYフラグが立ってしまう。


,砲弔い討STM32の全種のリファレンスマニュアルに明記されて
おります。スタートビットを自ら生成することによってマスターに
切り替わることになります。
データのやり取りが終わったら自動的にスレーブに戻ります。

で、このスレーブとしての動作中に外部からスタートビットに相当
する信号が偶然生成されてしまうとBUSYフラグがおっ立ってしまう
ということになります!!!!

これは内部操作からにも波及し、△両態が成立してしまいます。
つまりGPIOの操作をするだけでBUSYフラグがおっ立ってしまうわけです。


△隆岼磴辰深蟒腓龍饌療例とは、使用するI2CのGPIOポートのAlternate
Functionをする前にI2Cのクロックを有効にしてしまうことです。
これをやるとSTM32F1,STM32F2,STM32F4では100%の確率でBUSYフラグが
おっ立ってしまいます…もうビンビンに…!


実際にBUSYフラグが立ってしまった画像です。
STM32F411-Nucleoで確認していますがI2Cのクロックを有効にした
後にGPIOの設定を行うとI2Cの設定を何もしてないのにSRレジスタが
2(BUSYフラグON)になってしまいます。



そんなわけでBUSYフラグをおっ立てずにI2Cを確実に動作させる手順は
以下のようになります。

1.GPIOクロック有効
2.GPIOの設定(AF含む)
3.I2Cのクロック有効
4.I2Cの設定


上記順番を守るといきなりI2Cが動…かねぇ!という悲劇の発生確率が
下がると思います。特にバグコード生成器のCubeMXを使う際は生成
コードがこの順番を守っているか確認し、さらにオシロスコープで
実際のI2Cバスの波形を必ず確認するようにしてください!!!

ちなみにSTマイクロの日本サイトも初期化順番の注意点にとして
同じような記述
があります。

私の知る限り誤った手順でBUSYが立ってしまう品種はタイミングレジスタの
記述が簡素なSTM32F1,STM32F2,STM32F4,STM32L1のみとなりますが、
確実な動作のために他の品種でも上記初期化手順を守るべきだと思います。

Go to top of page