はじめに
前回の投稿から6年ほど経過した。
https://qiita.com/gkmaro634/items/7b2ce4f72615c96f41d3
その間STM32の開発環境も様変わりしているようで、時代に追いつくために今風の開発環境で何か試そうと思い立った。
前回の記事で取り上げたCubeMXがIDEとして取り込まれたCubeIDEというものがSTMicroからリリースされていたようで、今回こちらを使うこととする。インストール方法はネット上にたくさん情報があり、それらを参考にした。
単なるLチカでは物足りないので色々物色していたところテープLEDなるものが目に止まり、こちらの制御にトライすることにした。一筋縄では行かなかったのでその経緯を記事に残すこととした。
今回使用したもの
- NucleoF103: https://www.switch-science.com/catalog/1618/
- NeoPixel: https://www.switch-science.com/catalog/5209/
- Grove用ジャンパケーブル: https://www.switch-science.com/catalog/1048/
NeoPixelの制御
NeoPixelは素のSPIやI2Cでは制御できない独特な制御仕様となっている。
SPIを使ってNeoPixelへのHigh/Lowの制御信号を複数bitで表現する方法が紹介されており、今回はSPIでの制御を採用した。
https://www.newinnovations.nl/post/controlling-ws2812-and-ws2812b-using-only-stm32-spi/
https://blog.handen.net/archives/19087339.html
PWMを使って制御する方法もあるようだが、まだ試せていない。
https://qiita.com/kanehara/items/0e7776c225f83a6b4152
ピンアサイン、クロックの設定
今回用いるNeoPixelは電源(5V)、GND, 信号線をマイコンと接続すれば良い。
信号線はPB15(SPI2_MOSI)に接続することとした。
NeoPixelへのHigh/Lowの信号は1.25usの期間でのDuty比で決定される。
データシートに記載のしきい値に対して実際は多少のマージンを持っているようである。
https://qiita.com/mml/items/770bcf8b0f82a6ec9ad1
ひとまずSPI2へのクロックは8MHzとなるようCubeIDEで設定することとした。
NeoPixelへの制御は
5 / 8MHz = 0.625us
3 / 8MHz = 0.375us
であるので
High: 0b11111000
Low: 0b11100000
とHigh/Lowの制御信号を8bitで表現すればよさそうである。
プログラムの例
メイン関数から呼び出すメソッドを抜粋して記載する。
#define NEOPIXEL_0 0b11000000
#define NEOPIXEL_1 0b11111100
#define NEOPIXEL_RESET 0b00000000
#define NEOPIXEL_COUNT 30
#define NEOPIXEL_SIGNAL_CLOCK_COUNT 8
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} RGB;
RGB g_neoPixels[NEOPIXEL_COUNT];
void UpdateNeoPixelBuffer(uint32_t pixelIndex, RGB newValue){
if (pixelIndex >= NEOPIXEL_COUNT){
return;
}
g_neoPixels[pixelIndex].r = newValue.r;
g_neoPixels[pixelIndex].g = newValue.g;
g_neoPixels[pixelIndex].b = newValue.b;
}
void TurnOffNeoPixelBuffer(uint32_t pixelIndex){
if (pixelIndex >= NEOPIXEL_COUNT){
return;
}
g_neoPixels[pixelIndex].r = 0;
g_neoPixels[pixelIndex].g = 0;
g_neoPixels[pixelIndex].b = 0;
}
void ResetNeoPixel(){
uint8_t txValue = NEOPIXEL_RESET;
uint32_t i = 0;
// SPI Clock: 8MHz
// Send Low >50us
// 8 * 50 / 1e-6 = 400
for (i = 0; i < 512; i++){
HAL_SPI_Transmit(&hspi2, &txValue, 1, 100);
}
}
void UpdateNeoPixel(){
uint32_t i, j = 0;
uint8_t txValue;
ResetNeoPixel();
for (i=0; i < NEOPIXEL_COUNT; i++){
for (j=0; j<NEOPIXEL_SIGNAL_CLOCK_COUNT; j++){
txValue = g_neoPixels[i].g & 0x80 >> j ? NEOPIXEL_1 : NEOPIXEL_0;
HAL_SPI_Transmit(&hspi2, &txValue, 1, 100);
}
for (j=0; j<NEOPIXEL_SIGNAL_CLOCK_COUNT; j++){
txValue = g_neoPixels[i].r & 0x80 >> j ? NEOPIXEL_1 : NEOPIXEL_0;
HAL_SPI_Transmit(&hspi2, &txValue, 1, 100);
}
for (j=0; j<NEOPIXEL_SIGNAL_CLOCK_COUNT; j++){
txValue = g_neoPixels[i].b & 0x80 >> j ? NEOPIXEL_1 : NEOPIXEL_0;
HAL_SPI_Transmit(&hspi2, &txValue, 1, 100);
}
}
}