2017年02月06日

静電容量センサ

今回はSTM32での静電容量センサについて紹介します。


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


静電容量センサとは、接点等を使用せずに静電容量を監視することで、
人などの指が触れたかどうかを判断するセンサです。

エレベータのボタンや機器のボタンなどに使用されています。
電極を露出しなくとも、筺体に触れるだけでタッチ操作、
複数電極の組み合わせで旧iPodのようなスクロール操作等が可能になります。


STM32では標準で静電容量センサ機能(TSC:Touch Sensing Controller)が
搭載されているため、使用方法の概要を紹介します。


CubeMX上で「TSC」の項目から設定します。

TSCはセンサグループが分かれており、
グループ毎に設定や読み取り等を行います。


tsc-setting.png


グループ毎に必ずサンプリングキャパシタを接続する必要があります。
サンプリングキャパシタのIOには基準となるコンデンサを接続します。
サンプリングキャパシタの容量を変更することで、
タッチセンサの感度が変わります。
そのため、グループの中で1つはサンプリングキャパシタに接続するため、
グループ内のすべてのIOをタッチセンサで使用できない仕様です。


今回はG1_IO_1にサンプリングキャパシタとして0.1uFの積セラを接続し、
GNDへ落としました。

STM32L152 DiscoverySTM32F072 Discoveryのタッチセンサでは
47nFをサンプリングキャパシタとして使用していました。


tsc-cubemx.png

パラメータ詳細設定ではMax Count Valueが小さいと
上手く値が取得できないため、要注意です。
上記では16383に設定しました。


コードとしては
初期化で
MX_TSC_Init();

while(1)内では

   htsc.Init.ChannelIOs = TSC_GROUP1_IO2;//ADD for multi ch read
   HAL_TSC_Init(&htsc);                              //ADD for multi ch read
     HAL_TSC_IODischarge(&htsc, ENABLE);
     HAL_Delay(1);


      if (HAL_TSC_Start(&htsc) != HAL_OK)Error_Handler();
      while (HAL_TSC_GetState(&htsc) == HAL_TSC_STATE_BUSY)
          {           
          }

      __HAL_TSC_CLEAR_FLAG(&htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE));

      if (HAL_TSC_GroupGetStatus(&htsc, TSC_GROUP1_IDX) == TSC_GROUP_COMPLETED){
          Value1 = HAL_TSC_GroupGetValue(&htsc, TSC_GROUP1_IDX);
      }


      htsc.Init.ChannelIOs = TSC_GROUP1_IO4; //ADD for multi ch read
      HAL_TSC_Init(&htsc);                               //ADD for multi ch read
      HAL_TSC_IODischarge(&htsc, ENABLE);
      HAL_Delay(1);

      if (HAL_TSC_Start(&htsc) != HAL_OK)Error_Handler();
      while (HAL_TSC_GetState(&htsc) == HAL_TSC_STATE_BUSY)
       {        
        }

       __HAL_TSC_CLEAR_FLAG(&htsc, (TSC_FLAG_EOA | TSC_FLAG_MCE));

      if (HAL_TSC_GroupGetStatus(&htsc, TSC_GROUP1_IDX) == TSC_GROUP_COMPLETED){
         Value2 = HAL_TSC_GroupGetValue(&htsc, TSC_GROUP1_IDX);
       }

      printf("Value: %d, %d \n\r",Value1,Value2);


本来、複数のタッチセンサを読み出す場合はDMA等を使用するのが正しい方法ですが、
今回は実験のため、IO設定とHAL_TSC_Initを行うことで
強制的に複数chを読み出しました。




tsc-setting2.png



電極部に指を近づけると値が小さくなりました。


電極部のサイズ、配線の引きまわし、
基準となるサンプリングキャパシタ(Cs)などの要素を調整しないと
誤検知なく、反応のよいタッチセンサは難しいと思います。



また、次回、タッチセンサ設計ガイドラインの
データシート[PDF]を参考にしながら、
ここらへんを実験してみたいと思います。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2017年01月21日

磁気エンコーダTLE5012B

今回はInfineon製の磁気エンコーダTLE5012Bに試食について
紹介させて頂きます。


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


ProjectionBall等のロボット制御では
これまでAMS製磁気エンコーダAS5048AやAS5047Dなどが
性能に優れるため、好んで使用してきました。


ただ、値段が安く更に高性能化できないかと
色々なメーカの磁気エンコーダを探していました。


ちょっとシリアル通信に癖があるものの、
現行のAMS製AS5048AやAS5047Dなどに比べて
少し性能が高く、安いエンコーダを見つけたため、
試食してみました。



性能比較
◆AMS製AS5048A、AS5047D
-分解能:14bit (16384bit/rev)
-サンプリング周期:100us
-通信:SPI
-最大エラー角:0.7~1.4°
-価格:約900円/1個

◆Infineon製TLE5012B
-分解能:15bit (32768bit/rev)
-サンプリング周期:42.7us
-通信:SSC
-最大エラー角:1.0°
-価格:約600円/1個



単純な比較では性能はほとんど同じですが、
注目すべきはTLE5012Bのサンプリング周期が
42.7usということです。


アナログエンコーダやインクリメンタルエンコーダであれば、
制御周期の上限はないようなものですが、
シリアルエンコーダの場合はエンコーダのサンプリング周期で
制御周期が決まるといっても過言ではありません。

エンコーダのサンプリング周期以上に制御周期を速くしても、
同じ位置を返してしまうため、意味がありません。
TLE5012Bのサンプリング周期が42.7usと非常に高速なため、
他の性能が同様であれば、
サンプリング周期だけでも大幅に性能向上が期待できます。



ただ、TLE5012Bの問題点は少し癖のあるインタフェースということです。
SPIでなく、SSCというインタフェースを採用しています。

SCKやCSはSPIと同様ですが、
MISOとMOSIがそれぞれ1線でなく、1線で送信と受信を行います。
まるでSPIとI2Cを掛け合わせたような仕様です。


ただ、I2CのようなACK等はありません。
そのため、SPIのドライバを使用して通信します。
IOを入力と出力とその都度、切り替えては処理が遅くなるため、
下記のようにハードウェアで1線化しました。

TLE5012B_.png

そのままSPIのMISOとMOSIを接続すると、
PushPullのため、マイコン側の出力が短絡してしまいます。
そのため、抵抗を挟んで対処しました。


また、MISO側の抵抗を1k、MOSI側を10kに設定することで
マイコン側の出力とエンコーダ側の出力で
エンコーダ側を優先できるようにしてあります。
※マイコンの出せる電流値とエンコーダの電流値のバランスで決まるため、
マイコンに応じてオシロでHighとLowの電圧値を確認すること。
STM32F303では上記でHigh2.8V前後のいい感じになりました。


今回の試食ではTLE5012Bを2つ接続して、
CSで2つを切り替えて取得してみました。
CS以外のSCK、MOSI,MISOは共通配線です。

また磁石はAS5048A、AS5047D同様、こちらの磁石が使用可能です。

SPIの設定は下記の通り。
ポイントは16bitデータ転送、SPI_PHASE_2EDGEです。




static void MX_SPI1_Init(void)
{

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }

}



while(1){

   /* Encoder A*/
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,0);//CS A LOW
     
   sData[0]=0x8021;//SSC Command to read the angle value
      HAL_SPI_TransmitReceive(&hspi1,sData,rData,1,100);
     
   sData[0]=0x0000;//Empty Data     
     HAL_SPI_TransmitReceive(&hspi1,sData,rData,1,100);
      res=rData[0]&0x7FFF;//15bit

      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,1);//CS A HIGH
      asm("NOP");
   
   /* Encoder B*/
      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,0);//CS B LOW
     
   sData[0]=0x8021;//SSC Command to read the angle value
      HAL_SPI_TransmitReceive(&hspi1,sData,rData,1,100);
     
   sData[0]=0x0000;//Empty Data
      HAL_SPI_TransmitReceive(&hspi1,sData,rData,1,100);
      res1=rData[0]&0x7FFF;//15bit

      HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,1);//CS B HIGH
   


   printf("POS: %d %d\n",res,res1);
      HAL_Delay(300);
}



下記のように2つのTLE5012Bでそれぞれのエンコーダ値を取得することができました。


TLE5012B-uart.png


ちょっと癖のあるSSC通信ですが、
SPI通信ベースに実装できました。


TLE5012BはAS5048Aなどのように有名ではないようで、
Web上で調べてもほとんど情報がありませんでした。
メーカのHPではSPI互換といっているが、
どのような実装を想定しているのか少し疑問です。




今回は高速周期でのテストはしていませんが、
ロボットに組込んでどれくらい性能に差があるか
今後、実験してみたいと思います。


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

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) | 電子工作 | このブログの読者になる | 更新情報をチェックする