2016年12月03日

GPIOを介したSDカード読み書き


今回はSTM32でのGPIOでSDカードの読み書きについて紹介します。
特にSTM32F303K8等ではSPIが1つしかなく、SDIOもないため、
CubeMXで自動生成されず、
SDカードの読み書きに苦労するところです。

ChanさんのFatFSモジュールを使用して
STM32のGPIOによるSDカード読み書きを紹介します。

読み書きの速度に期待はできないものの、
空きのGPIOで自由に使用可能なため、
汎用性は非常に高いです。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。


CubeMXでSDカードで使用するGPIOを定義します。
最低限はCS,CLK、MOSI、MISOの4つです。
MISOのみInput、他はOutputで定義します。
プルアップ及びハイスピード設定を有効にします。

今回は
CS:PA11(D10)
CLK:PB5(D11)
MOSI:PB4(D12)
MISO:PB3(D13)
で定義しました。

※Chanさんのモジュールを使用するため、
  CubeMX自体のFatFSはチェックをいれません。


下の方にある「サンプルプロジェクト」をダウンロードします。

ffsampleフォルダ内のgenericフォルダのコードを使用します。
Workspace内のIncとSrcにそれぞれ下記のファイルを入れます。
diskio.h、ff.c、ff.h、ffconf.h、interger.h、sdmm.c


STM32ですぐ使えるように設定変更します
◆ffconf.h
 //RTC無効化
 #define _FS_NORTC    1
 //LFN無効化
 #define    _USE_LFN    0

◆sdmm.c

#define DO_INIT()                             
#define DO            HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)

#define DI_INIT()                              
#define DI_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_SET)
#define DI_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET)

#define CK_INIT()                              
#define CK_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET)
#define CK_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET)

#define CS_INIT()                          
#define CS_H()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET)
#define CS_L()        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET)



dly_us (UINT n)関数内のPINB;をコメントアウト

select (void)関数が名前が重複するため、
適当な他の名前に置き換え、sdmm.c内のselectを置き換えた名前に2か所変更。

#include <avr/io.h> をコメントアウトして#include "stm32f3xx_hal.h" に変更。


あとは、main.c内で
#include "ff.h"をインクルード

FATFS FatFs;      
FIL Fil;           
を変数定義

while(1)の中で下記を実行。


     int bw;
           if(f_mount(&FatFs, "", 1)==FR_OK)printf("Mount OK!! \n");
           else {
               printf("Mount Fail!! \n");
               while(1);
           }
                  
           if (f_open(&Fil, "newfile.txt", FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) {  

                   f_write(&Fil, "It works!!!!!!!\r\n", 16, &bw);   
                   f_close(&Fil);                               

                   if(bw==16)printf("Write OK!! \n");
                   while(1);

            }
      else{
                   printf("Open Fail!! \n");
                   while(1);
            }
これでGPIOを使って書き込みができます。
GPIOの書き込みは思った以上に簡単にできました。



posted by Crescent at 00:00| Comment(1) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2016年11月04日

Beaglebone Node-Redインストール

今回はBeagleboneblackを使用して
Node-Red + Freeboardをインストールする方法をメモとして記します。


世の中は既にRaspberryPIの一強という感じですが、
BeagleboneblackはPCに接続して使用することを前提としているため、
使い方が大きくRaspberryPIと違います。

RaspberryPIはキーボードやマウス、ディスプレイが必須ですが、
BeagleboneblackはPCに接続し、ドライバをインストールすると標準で
USBネットワークとして認識し、
ブラウザやSSH経由でキーボードやマウス、ディスプレイなくとも簡単に
PCから開発やプログラミングができます。
この点が非常に気に入っています。



今回インストール方法をご紹介するNode-Redはブロックを線で繋げるだけで
簡単にプログラミングできるソフトウェアです。

簡単にIoTなものを作ることができます。



ただ、Beagleboneblack標準のDebianでは
最低限のNode-Redのみインストールされており、
BeagleboneblackのIO等は別途インストールが必要なようです。


Beagleboneblackの現時点でのOSイメージ
bone-debian-8.4-lxqt-4gb-armhf-2016-05-13-4gb
Node-RedインストールやBeagleboneblack用のノードインストール
の方法ではエラーが発生していまい上手くインストールできませんでした。

Node.js系はバージョンが少し変わるだけで色々エラーの嵐で
上手くいけば強力で非常に便利ですが、
上手くいかないとバージョン入れ替えなどの試行錯誤が必要で
初心者には敷居が高いイメージがあります。



下記の方法でエラーを回避できたため、
メモとしてご紹介します。


「サービス終了」
sudo service node-red stop

「アンインストール」
sudo npm uninstall -g --unsafe-perm node-red-node-beaglebone
sudo apt-get purge bb-node-red-installer

「インストール」
sudo apt-get install bb-node-red-installer


ついでに「Freeboard」のインストールはバージョン指定でインストールします。
現時点の0.0.7ではエラーが発生して上手くインストールできませんでした。

「Freeboard」
sudo npm install -g node-red-contrib-freeboard@0.0.6


これでセンサデータの読み取りやロギング、可視化が簡単に実現できます。

具体的な使用例はまた後ほどご紹介します。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2016年10月23日

複数chのAD変換

今回はSTM32でのDMAを使用したAD変換について紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F3 ver. 1.60)
です。

複数chをAD変換する場合はDMA転送を前提とするようです。
STM32CubeMXは下記のように設定してコードを出力しました。

ADSetting.png

dma.png


コード例は下記の通り。
こちらのサイトのコード例を参考にさせて頂きました。

・変数系定義

uint32_t  ADCValue;
int Number;
enum{ LENGTH = 1024 };
uint16_t ADCBuffer[LENGTH];

・AD変換後処理関数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  //After AD Convert
}


・初期化後にADCの読み込みを開始させる
HAL_ADC_Start_DMA(&hadc1, ADCBuffer, LENGTH);


・AD変換の設定部分

  /**Configure Regular Channel
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 1;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure Regular Channel
    */
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }


コードを書いてAD変換したところ、
1ch目しか読み込めない不具合に遭遇。

自動生成されたConfigure Regular Channelの部分を確認すると、
赤文字のch2の設定が部分的に抜けていました。

自動生成後されたCubeMXのコードは1ch目の設定のみ
されている状況で、2ch目については何も書かれていませんでした。

赤文字部分を追加して、再度実行すると
2ch分、出力されることが確認できました。


F3 CubeMXの不具合かもしれません。
今回の不具合を回避するような設定方法があるかもしれませんが、
希望通りの動作でない場合は自動生成された部分含めて、
地道にコードを確認することが必要ですね。



また、AD変換の注意点として、
DMA転送のバッファ定義(上の例ではADCBuffer)は
変数定義をuint16_tかint16_tの16bit長にすることです。

DMA設定で
Memory Increment: Enable
Memory Data Width: Half Word (16bit)
では16bitずつバッファメモリをシフトさせるため、
uint32_tなどの32bit長にすると
バッファが破壊されてAD変換値が桁外れに大きな値になってしまいます...


posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2016年10月13日

SPI/I2C シリアル通信

今回はSTM32でのSPI/I2C シリアル通信について紹介します。

環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX
です。


◆HALライブラリを使用したI2C通信
HALライブラリを使用したI2C通信でSTM32側がマスタの場合、
PICマイコンなどのようにACKとかRepeatStartなど考える必要はありません。
下記の読み込みか書き込みかの関数を使い分けるだけです!

HAL_I2C_Mem_Read( I2C_HandleTypeDef *hi2c,
                  uint16_t DevAddress,
                  uint16_t MemAddress,
                  uint16_t MemAddSize,
                  uint8_t *pData,
                  uint16_t Size,
                  uint32_t Timeout);

HAL_I2C_Mem_Write( I2C_HandleTypeDef *hi2c,
                  uint16_t DevAddress,
                  uint16_t MemAddress,
                  uint16_t MemAddSize,
                  uint8_t *pData,
                  uint16_t Size,
                  uint32_t Timeout);



非接触赤外線センサMLX90614を読みだした場合
#define DevAddress  (0xB4)
⇒0x5A
として読み書きされます。
R/Wを含めてアドレスを指定するようです。

環境温度読み込み時の例は下記の通り。


HAL_I2C_Mem_Read( hi2c,
                  (uint16_t)DevAddress,
                  0x06,
                  I2C_MEMADD_SIZE_8BIT,
                  (uint8_t*) ret,
                  3,
                  100);

res=(float)(ret[0]+(ret[1]<<8))/100.0;
resに環境温度が格納されます。

MLX90614_i2c-STM32.png

LabToolを使用してデータ通信状態。
直接LabToolをI2Cラインへ接続するとインピーダンスが変わってしまうため、
1k~10kの抵抗を挟んでロギングしました。

I2Cはアドレスの表記が7bitアドレスでR/Wを含めるか、含めないか
チップメーカによってまちまちなのでそこがいつも引っかかるところです。

また、マイコンがいつもと違うと同様にまちまちなので
今回のような地味な検証が後々役立ちます。


思った以上にすんなり動いてくれました。



◆HALライブラリを使用したSPI通信
I2C同様、SPIも思った以上にシンプルでした。
デバイスのデータシートと睨めっこしながら、
main.c内のMX_SPI1_Initの設定を確認してください。
通信できない時はSPIのクロック極性やサンプル位置設定が
間違っていることがほとんどです。

hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;//SPI_POLARITY_HIGH//?!
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;//SPI_PHASE_1EDGE//?!


HAL_SPI_TransmitReceive( SPI_HandleTypeDef *hspi,
                      uint8_t *pTxData,
                      uint8_t *pRxData,
                      uint16_t Size,
                      uint32_t Timeout);

実際にあるデバイスで通信した場合
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,0);//CS
HAL_SPI_TransmitReceive(&hspi1,sData,rData,1,100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,1);//CS


送受信を想定してTransmitReceiveを使用していますが、
受信専用の関数
HAL_SPI_Receive( SPI_HandleTypeDef *hspi,
               uint8_t *pData,
               uint16_t Size,
               uint32_t Timeout);
送信専用関数
HAL_SPI_Transmit( SPI_HandleTypeDef *hspi,
                uint8_t *pData,
                uint16_t Size,
                uint32_t Timeout);
もあります。
ただ、実際に実験してみてはいませんが、
どちらかの場合でも送受信関数TransmitReceiveで
空データを使うことで同じ関数でいけそうな気がします。



SPIについても思った以上にすんなり動いてくれました。

PICの時はRepeatStartのタイミングなど色々苦労させられましたが、
STM32のHALライブラリのシリアル通信は思った以上に考えることが少なく、
すんなり動いてくれました。



posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2016年10月05日

PrintfのFloat出力

以前にSTM32マイコンでPrintfを出力する方法を紹介させて頂きました。

だた、紹介した方法だけではfloatの出力ができないという問題があります。
設定せずにfloat出力すると空欄になってしまいます。

そのため、今回はPrintfでFloat出力する方法をご紹介します。



PrintfでFloat出力する方法を調べると既にいくつか紹介されていました。




ただ、開発環境が異なるため、
今回はSystem Workbench for STM32 (SW4STM32)での
PrintfでFloat出力する方法を紹介します。



結論から言うとプロジェクト内のプロパティから、
「C/C++ Build」->[Settings]->
[Tool Settings]->[MCU GCC Linker]->[Miscellaneous]
でLinker flagsの
「-specs=nosys.specs -specs=nano.specs」
から
「-u _printf_float」
を追加して
「-specs=nosys.specs -specs=nano.specs  -u _printf_float」
にすることです。


float.png


これでビルドするとPrintfでfloat出力ができるようになります!



他の開発環境では別に色々設定変更が必要なようですが、
SW4STM32ではデフォルトのままで大丈夫でした。

float2.png

一応確認すると既にFPU等の設定は設定済みでした。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする