2017年02月20日

デジタルシグマ型ADC読込

今回はデルタシグマ型ADC(SDADC)について紹介させて頂きます。

デルタシグマ型ADC(SDADC)は、
一般的な内臓の逐次比較型に比べ、
変換速度は遅いものの、ノイズが少なく、高分解能にAD変換できる特徴があります。

・デルタシグマ型ADC(SDADC)
16bit
16.6 ksps @マルチチャンネル変換
50 ksps @1チャンネル変換
差動信号入力可

・逐次比較型ADC
12bit
最大1Msps
シングル信号のみ


今回はSTM32F373に内臓のSDADCを使用してみました。
HALライブラリで公開されているサンプルは1chのみですが、
2ch読み込みを実験してみました。




環境はSDADC内臓の373シリーズ
・STM32F373CC
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F3 ver. 1.60)
です。


◆CubeMXの設定としては、
PB1→SDADC1_AIN5P
PB2→SDADC1_AIN4P


CubeMXで自動生成される初期化関数は下記の通りです。
static void MX_SDADC1_Init(void)
{

  SDADC_ConfParamTypeDef ConfParamStruct;

  hsdadc1.Instance = SDADC1;
  hsdadc1.Init.IdleLowPowerMode = SDADC_LOWPOWER_NONE;
  hsdadc1.Init.FastConversionMode = SDADC_FAST_CONV_DISABLE;
  hsdadc1.Init.SlowClockMode = SDADC_SLOW_CLOCK_DISABLE;
  hsdadc1.Init.ReferenceVoltage = SDADC_VREF_VDDA;
  if (HAL_SDADC_Init(&hsdadc1) != HAL_OK)
  {
    Error_Handler();
  }


  ConfParamStruct.InputMode = SDADC_INPUT_MODE_SE_ZERO_REFERENCE;
  ConfParamStruct.Gain = SDADC_GAIN_1;
  ConfParamStruct.CommonMode = SDADC_COMMON_MODE_VSSA;
  ConfParamStruct.Offset = 0;
  if (HAL_SDADC_PrepareChannelConfig(&hsdadc1,
                      SDADC_CONF_INDEX_0,
                      &ConfParamStruct) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_SDADC_PrepareChannelConfig(&hsdadc1,
                     SDADC_CONF_INDEX_1,
                     &ConfParamStruct) != HAL_OK)
  {
    Error_Handler();
  }

}


◆割り込み関数
追加する割り込み処理として下記の関数を追加します。
void HAL_SDADC_InjectedConvCpltCallback(SDADC_HandleTypeDef *hsdadc)
{
     int16_t tmp=HAL_SDADC_InjectedGetValue(hsdadc,
                      (uint32_t *) &Channel);
    if(Channel==5)InjectedConvData=tmp;       //PB1
    else if(Channel==4)InjectedConvData2=tmp;  //PB2
}



◆define例として

#define SDADC_RESOL           (uint32_t) 65535
#define SDADC_INIT_TIMEOUT    30
#define SDADC_CAL_TIMEOUT     4*30720
#define SDADC_VREF2            (float) 3.300
#define SDADC_GAIN            (uint32_t) 1 

◆グローバル変数

 float inputVoltage = 0;
 int16_t ConvData = 0;
 uint32_t Channel = 0;

 float inputVoltage2 = 0;
 int16_t ConvData2 = 0;
 uint32_t Channel2 = 1;

mainコード内の例として、

MX_SDADC1_Init();
初期化実行後、詳細設定とキャリブレーション、割り込み開始を実行します。

if (HAL_SDADC_AssociateChannelConfig(&hsdadc1,
                SDADC_CHANNEL_4|SDADC_CHANNEL_5,
                SDADC_CONF_INDEX_0) != HAL_OK)
       {
                  printf("ERROR:  HAL_SDADC_AssociateChannelConfig");
       }

   
       if (HAL_SDADC_InjectedConfigChannel(&hsdadc1,
               SDADC_CHANNEL_4|SDADC_CHANNEL_5,
               SDADC_CONTINUOUS_CONV_ON) != HAL_OK)
       {
                printf("ERROR:  HAL_SDADC_InjectedConfigChannel");
       }


      if (HAL_SDADC_SelectInjectedTrigger(&hsdadc1,
              SDADC_SOFTWARE_TRIGGER) != HAL_OK)
    {
              printf("ERROR:  HAL_SDADC_SelectInjectedTrigger");
    }

  
    if (HAL_SDADC_CalibrationStart(&hsdadc1,
              SDADC_CALIBRATION_SEQ_2) != HAL_OK)
    {
              printf("ERROR:  HAL_SDADC_CalibrationStart");
    }

  
    if (HAL_SDADC_PollForCalibEvent(&hsdadc1,
              HAL_MAX_DELAY) != HAL_OK)
    {
           printf("ERROR:  HAL_SDADC_PollForCalibEvent");
    }

 
    if (HAL_SDADC_InjectedStart_IT(&hsdadc1) != HAL_OK)
    {
         printf("ERROR:  HAL_SDADC_InjectedStart_IT");
    }


while関数内では
 inputVoltage = (((ConvData + 32768) * SDADC_VREF2)
              / (SDADC_GAIN * SDADC_RESOL));
 inputVoltage2 = (((ConvData2 + 32768) * SDADC_VREF2)
              / (SDADC_GAIN * SDADC_RESOL));

 printf("SDADC PB1:%d %1.3f, PB2:%d %1.3f\n\r",
                                 ConvData,
                                 inputVoltage,
                                 ConvData2,
                                 inputVoltage2);
 HAL_Delay(300);



可変抵抗をつけて実際に読み込んでみました。

SDADC.png


確かに値は安定して変化しているようです。

今回は差動入力でなく、シングル入力ですが、
値としては差動として読み込まれるようで、
0V入力で-32768、3.3入力で32768となりました。

温度計など大きく値がぶれない処理によいと思います。

ラベル:STM32 HAL 部品
posted by Crescent at 13:16| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

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月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月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) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2016年12月08日

math関数使用時のエラー対処

今回はSTM32でのmath関数使用時のエラー対処について紹介します。

以前にご紹介したDSP処理の前設定2でも同じようなエラーがでましたが、
以前はDSPを使用した関数でした。
今回はDSP関数は使用せず、math関数使用時のエラー対処方法です。



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


main.c関数中でpowなど使う分には問題ありませんでしたが、
読み込んだヘッダやドライバ系にpow関数を使うと
下記のようなエラーが発生してコンパイルできませんでした。


w_pow.c:(.text.pow+0x): undefined reference to `__errno'
collect2.exe: error: ld returned 1 exit status
make: *** [STM32F.elf] Error 1


Eclipseのプロジェクトから
「C/C++ Build」→「Settings」→「ToolSettings」→「MCU GCC Linker」
「Libraries」に下記のように
m
を追加します。


ffff.png


上記のようなエラーに遭遇するパターンがいくつかあるため、
今回はmの追加で回避できました。

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