2017年01月26日

内臓DAC出力

今回はSTM32でのDMAなしのDA変換について紹介します。


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

マイコン内蔵のDACというと、
キャリブレーションやブザー用などに付いてるオマケ程度で
応答や分解能など性能は全く期待できないという印象でした。


STM32F3のDACは8bit、12bitの分解能が選択でき、
Settlingタイムも3usと高速で外付け汎用DACと変わらない性能が
内臓DACで実現されています。

つまり、DACを外付けしなくとも、
モータ制御等に使えそうです。




STM32CubeMXは下記のように設定してコードを出力しました。


PA4 DAC1OUT1
PA5 DAC1OUT2

dac.png



割り込み等を使わずに単に出力するだけの場合は非常に簡単です。

サンプル出力例は下記の通りです。
階段状の出力を2ch独立に出してみました。

MX_DAC1_Init();

HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Start(&hdac1, DAC_CHANNEL_2);


 int i=0;

  while (1)
  {
 
      if(i>3600)i=0;
      else i=i+300;
      HAL_DAC_SetValue(&hdac1, 
                   DAC_CHANNEL_1,
                   DAC_ALIGN_12B_R,
                   i);
      HAL_DAC_SetValue(&hdac1, 
                   DAC_CHANNEL_2,
                   DAC_ALIGN_12B_R,
                   4096-i);

      printf("DA TEST: %d \n",i);
      HAL_Delay(200);
 

  }


思った以上にシンプルな関数構成でDAC出力することができました。

内臓DACを使用してサーボアンプの制御なども色々出来そうです。
そのうち、試してみたいと思います。
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) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2017年01月16日

RTC使い方

今回はSTM32のRTC機能について紹介します。


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

48ピン以上の多くのMPUにはVBAT端子がついており、
32.768kHz水晶発振子と電池を接続するとRTC機能が使用可能です。

また、RTCの他に64byte程度のバックアップレジスタがあり、
電池で値を保持することが可能です。
例えば、ボタンや機器の状態等を保持することに使えそうです。


STM32CubeMX上でRTCを有効化し、
ActivateClockSourceとActivateCalenderにチェックを入れます。



◆設定

RTC_TimeTypeDef sTime;
sTime.Hours = 11;
sTime.Minutes =12;
sTime.Seconds = 13;
sTime.SubSeconds = 0;
sTime.TimeFormat = RTC_HOURFORMAT_24;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;

RTC_DateTypeDef sDate;
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 15;
sDate.Year = 0;//YEARは2017でなく、ある年を基準として設定します


HAL_RTC_SetTime(&hrtc, &sTime, FORMAT_BCD);
HAL_RTC_SetDate(&hrtc, &sDate, FORMAT_BCD);

で時刻や日付をセットします。


◆読み出し

RTC_TimeTypeDef gTime;
RTC_DateTypeDef gDate;
HAL_RTC_GetTime(&hrtc, &gTime, FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &gDate, FORMAT_BIN);
printf("YY MM DD : %d , %d, %d,  ",gDate.Year,gDate.Month,gDate.Date);
printf("HH MM SS : %d , %d, %d \n",gTime.Hours,gTime.Minutes,gTime.Seconds);

で時刻や日付を読み出し可能です。

◆バックアップレジスタ
バックアップレジスタは下記の関数で読み書き可能です。

書き込み
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0,0x01234567);
読み出し
uint32_t ROM=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);



外付けでRTCを別途つける必要なく、
水晶発振子と電池だけで簡単にRTC機能とその周辺の機能を実装可能です。
他にADCの設定でVBATの電源電圧の読み込みが可能なようです。


実験していて驚いたこととして、
一度、基板上の電池と32.768kHz水晶発振子を接続しておけば、
コードに直しても設定が保持され、時刻も進んでいることです。

MPU内のプログラムをRTCとは関係のないコードを書き込んでも
再度、RTCのプログラムに戻すと保持されていました。


一般的なMPUのRTCは別のプログラムを書き込んだ時点でRTCが停止し、
時刻やデータが消えてしまうのが一般的です。
実験のボードで使うにもSTM32は時刻やバックアップレジスタが保持されるのは
再設定の手間が省けて便利な機能だと思いました。
VBATとして電池端子が独立しているのも扱いが楽だと思いました。

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

2017年01月11日

SMD部品の取り外し

今回はSMD部品(表面実装部品)を取り外しの実験についてご紹介します。


色々基板を試作していると失敗してしまうこともあります。
失敗した基板はともかく、実装した部品がもったいないと思っていました。
ただ、SMD部品の場合、取り外しで細く複数ある足を綺麗に外すのは
かなり難易度が高くなります。



SMD取り外し用の特殊な半田ごて工具を持ち合わせていないため、
安く綺麗に取り外す方法を検討していました。



今回は基板を再利用するのでなく、部品を再利用するため、
SMD部品の足をバーナーで熱して外す方法を試してみました。



バーナーといってもアウトドア用のバーナーは炎が太いため、
周辺のチップや基板を焼いてしまいます。
今回はポケトーチという小型で安価なバーナーを
ホームセンターで購入し、実験してみました。




小型でも1300度の炎になるため、
ある程度距離を離して全体をまんべんなくあぶる感じで。
5秒くらいあぶるとピンセットで横に動くようになり、
そのまま持ち上げると簡単に外れました。

img1.jpg


最初に加減が分からず、
熱し過ぎて基板のパターンが焼けて剥がれてしまいました(ピンセット先部分)。
他の部分は綺麗に取れました。

img2.jpg



今回外したチップはD-PAK、TQFP44pin、SSOP24pinです。
鉛フリーはんだのため、融点が高く取り外しに苦労すると思いましたが、
思った以上に簡単に外れました。
一部熱し過ぎてパッケージ部分が欠けてしまいましたが、
IC自体の動作には問題ありませんでした。


ただ、チップの耐久温度を超えている可能性が高いため、
動作保証はできないですね。
評価実験用のチップとして第2の人生を活躍してもらいたいと思います。



ポケトーチは炎が細いため、SMD部品の取り外しバーナーとしては
かなり使いやすいと思いました。


工具箱にあっても損はないツールの1つだと思いました。
バーナーは高温なのでやけどや火事には充分気をつけてください。

特にポケトーチはボタンから離しても
残留ガスで数秒火がついたままになるので、
要注意です。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | 電子部品 | このブログの読者になる | 更新情報をチェックする

2017年01月06日

UART受信について

今回はSTM32でのUART受信処理について紹介します。


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


STM32F3系でなかなかUartの受信ができず、
DMAを使うしかないかなと諦めかけていましたが、
原因が分かったため、紹介します。
(This report is wrote about
  not called HAL_UART_RxCpltCallback probrem)


CubeMXのUart設定でNVIC Settingの
USART Global InterruptをEnableにするとUART受信で
HAL_UART_RxCpltCallback
という関数が呼び出されます。


uart.png

ただ、HALライブラリのバグで呼び出せないという不具合がありました。
不具合ではなく、最終的に関数の呼び出しが必要と判明しました。
対処方法#1で受信できるようになります。

対処方法#1は下記の通りです。
(The coping process #1 is following)

@HAL_UART_RxCpltCallbackを追加。
  (ADD HAL_UART_RxCpltCallback func.)

char UART1_Data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
     if(UartHandle->Instance==USART1){
          HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);      
    }
 }

Amain関数内の初期化後にUartの設定と割り込み有効化
 (Set Uart variable and enable interrupt after init.)

HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);

while(1)
{
~~~



以上の設定を行うことで
1文字受信毎にHAL_UART_RxCpltCallbackが呼び出されました。

uart2.png



【参考 reference】
対処方法#2は下記の通りです。
(The coping process #2 is following)


@HAL_UART_RxCpltCallbackを追加。
  (ADD HAL_UART_RxCpltCallback func.)

char UART1_Data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
     if(UartHandle->Instance==USART1){
          HAL_UART_Receive_IT(&huart1, (uint8_t*) &UART1_Data, 1);      
    }
 }

Amain関数内の初期化後にUartの設定と割り込み有効化
 (Set Uart variable and enable interrupt after init.)

 //Initially pointer is not set!
  huart1.pRxBuffPtr=(uint8_t *)&UART1_Data;  //IMPORTANT!!
 huart1.RxXferCount=1;  //IMPORTANT!!
  huart1.Mask=255;  //If not set, lost first string.
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);

while(1)
{
~~~


Bstm32f3xx_it.c内のUSART1_IRQHandler関数を修正
 (Fix USART1_IRQHandler func in stm32f3xx_it.c)

void USART1_IRQHandler(void)
{
  huart1.RxState=HAL_UART_STATE_BUSY_RX;  //IMPORTANT!!
  HAL_UART_IRQHandler(&huart1);
}
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする