STM32F3DISCOVERY、加速度計、ステッピングモーター、そしてちょっとした魔法

こんにちは、ハブロフカニン。 私の仕事についてお話したいと思います。これは通常、工科大学の最後のコースの学生によって行われます(はい、それは文字「D」の悪い言葉です)。



この作業の目的は、移動ロボットを検知および制御するシステムを開発することでした。 これらの大きな言葉の背後にあることは、それほど大きなことではなく、私にとって興味深い仕事です。



ポイントに近い。 マイクロプロセッサ、センサーパック、ステップエンジンがあり、マイクロプロセッサーがセンサー(加速度計とジャイロスコープ)からデータを読み取り、この情報をPCに送信し、コンピューターからエンジンを制御するコマンドを受信し、エンジンを回転させる必要があります。



購入:



彼は次のコンポーネントを選択しました。



•STM32F3DISCOVERY。加速度計とジャイロスコープがすでに搭載されています。 また、STM32には既に多くの既製の例があり、これによりタスクが簡単になったはずです(部分的に簡単になりました)。

•デジタル加速度計LIS331DH、3軸、高精度(2g〜8g)。 一般に、LIS *シリーズのほぼ全体が非常に優れており、要件に適合しています。

•ここで、ステップエンジンFL42STH25-0404Aが、私のお気に入りの部署に横たわっていたものでした。



興味深い点は、作業の過程で、特にSTM32F3に関する記事と情報を検索し、それが予想ほどではなかったことに驚きました(たとえば、STM32F4によると、さらに多くの例と情報があります)。 はい、あなたはそこにほとんど違いがなく、あなたは部分的に正しいと言いますが、周辺機器との仕事はいくつかの場所で異なります。 そのため、このマイクロプロセッサを使用して5セントを稼ぐことにしました。



少しずつ理解しています。



ボックスからSTM32F3DISCOVERYを取得し、PCに接続して起動します。 デモプログラムは、偏差がある場合、つまりセンサーが動作している場合にランプが点滅することを示しています。 私たちは「万歳!」と叫び、必要なものを理解して実際に実装するためにコードに飛び込みます。

画像



そして、必要なものはたくさんありますが、最初は外部のセンサー(オンボードではない)に手を伸ばすのをやめることにしました。 はんだ付けされたアクセル、接続します。 アクセルには、接続用の2つのインターフェイスがあります:SPIとI2C。 SPIにとどまることにしました。 私はすでにATTINY2313でそれを処理する必要があり(プログラムで実装しました)、ハードウェアSPIにはまったく問題がないはずだと考えました。



もっと簡単にしたかったのですが、いつものように判明しました





接続:MISO-MISO、MOSI-MOSI、SCK-SCK、CSは、プログラムでプルするため、どの脚にも掛けることができます。

まず、SPIを初期化する必要があります。 この例では、組み込みのジャイロスコープが最初のSPIを介して動作するため(正確には覚えていないアクセル)、SPI2で作業が行われます。

void SPI2_Configuration(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_5); // SCK GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_5); // MISO GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); // MOSI SPI_InitTypeDef SPI_InitStructure; SPI_StructInit(&SPI_InitStructure); SPI_I2S_DeInit(SPI2); SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &SPI_InitStructure); /* Configure the Priority Group to 1 bit */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* Configure the SPI interrupt priority */ NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Initialize the FIFO threshold */ SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF); SPI_Cmd(SPI2, ENABLE); }
      
      





WHO_AM_Iレジスタからデータを読み取ろうとしています:

getValue = getRegisterValue(&AXELx, 0x0F);





どこで

 char getRegisterValue(AXEL_TypeDef* AXELx, char address) { AXELx->CS_Port->BRR = AXELx->CS_Pin; SPI_I2S_SendData16(SPI2, 0x8000|(address<<8)); while(!SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)); AXELx->CS_Port->BSRR = AXELx->CS_Pin; return SPI_I2S_ReceiveData16(SPI2); }
      
      





ここで、CSを地面に押すとデータ転送の開始が開始されるため、対処している加速度計のCSを引く必要がある重要なニュアンスに注意する必要があります(この瞬間のため、深刻なプラグと問題に加えて、すべてのアクセルが正常にはんだ付けされず、動作不能であることが判明したため、私の仕事は約1週間2回停止しました。O_o)。 次に、使用するレジスタのアドレスを送信し(読み取り/書き込み)、2番目のバイトを読み取りまたは書き込みます。



さて、次のように書きます。

 void setRegisterValue(AXEL_TypeDef* AXELx, char address, char data) { AXELx->CS_Port->BRR = AXELx->CS_Pin; SPI_I2S_SendData16(SPI2,((short)address<<8)|(short)data); while(!SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)); AXELx->CS_Port->BSRR = AXELx->CS_Pin; SPI_I2S_ReceiveData16(SPI2); }
      
      





正常な動作のために、センサーも初期化する必要があります。つまり、3つの軸すべてに沿って読み取り、動作周波数を示すことを示します(コントロールワードの意味とその形成については、データシートの軸を見てください)。

 void Axel_Init(AXEL_TypeDef* AXELx) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable Axel CS Pin */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Pin = AXELx->CS_Pin; GPIO_Init(AXELx->CS_Port, &GPIO_InitStructure); setRegisterValue(AXELx, 0x20, 0x27); }
      
      





センサーが完成したら、ほら! では、ステッピングモーター制御に移りましょう。



静かに行く-あなたは続けます





SDを制御するために、ドライバーVNH3SP30が使用されました。 確かに、ステッピングモーターの2つの巻線のうち1つだけを制御できるため、これらのショールのうち2つが必要です。



したがって、1つの巻線を制御するには、モーター全体に対してマイクロコントローラーから3つの出力(1つの搬送周波数と2つの方向)が必要です-6。



便宜上、ポートを定義します。

 #define A_PULSE_PORT GPIOB #define A_PULSE_PIN GPIO_Pin_2 #define A_DIR1_PORT GPIOB #define A_DIR1_PIN GPIO_Pin_0 #define A_DIR2_PORT GPIOE #define A_DIR2_PIN GPIO_Pin_8 #define B_PULSE_PORT GPIOE #define B_PULSE_PIN GPIO_Pin_12 #define B_DIR1_PORT GPIOE #define B_DIR1_PIN GPIO_Pin_10 #define B_DIR2_PORT GPIOE #define B_DIR2_PIN GPIO_Pin_14
      
      





そしてそれらを初期化します:

 void StepMotorSetup() { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOE, ENABLE); /*       */ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin = A_PULSE_PIN; GPIO_Init(A_PULSE_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = B_PULSE_PIN; GPIO_Init(B_PULSE_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = A_DIR1_PIN; GPIO_Init(A_DIR1_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = B_DIR1_PIN; GPIO_Init(B_DIR1_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = A_DIR2_PIN; GPIO_Init(A_DIR2_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = B_DIR2_PIN; GPIO_Init(B_DIR2_PORT, &GPIO_InitStructure); }
      
      





モーターで1ステップを実行するには、モーターの巻線を必要な順序でオンにする、つまり、制御信号をドライバーに適用する必要があります。



制御信号マスクは次のとおりです。

 u8 WireConFullStep[4][4] = {{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}};
      
      





そして今、私たちは正しい方向に一歩を踏み出します。 この場合の方向は、制御信号のマスクのバイパスの方向によって決まります。

 void DoStep(int index) { GPIO_ResetBits(A_DIR1_PORT, A_DIR1_PIN); GPIO_ResetBits(A_DIR2_PORT, A_DIR2_PIN); GPIO_ResetBits(B_DIR1_PORT, B_DIR1_PIN); GPIO_ResetBits(B_DIR2_PORT, B_DIR2_PIN); GPIO_ResetBits(A_PULSE_PORT, A_PULSE_PIN); GPIO_ResetBits(B_PULSE_PORT, B_PULSE_PIN); if(WireConFullStep[index][0] == 1) { GPIO_SetBits(A_DIR1_PORT, A_DIR1_PIN); GPIO_SetBits(A_PULSE_PORT, A_PULSE_PIN); } if(WireConFullStep[index][1] == 1) { GPIO_SetBits(A_DIR2_PORT, A_DIR2_PIN); GPIO_SetBits(A_PULSE_PORT, A_PULSE_PIN); } if(WireConFullStep[index][2] == 1) { GPIO_SetBits(B_DIR1_PORT, B_DIR1_PIN); GPIO_SetBits(B_PULSE_PORT, B_PULSE_PIN); } if(WireConFullStep[index][3] == 1) { GPIO_SetBits(B_DIR2_PORT, B_DIR2_PIN); GPIO_SetBits(B_PULSE_PORT, B_PULSE_PIN); } }
      
      







便宜上、反時計回りのステップと時計回りのステップを定義します。

 void CWStep() { DoStep(CurIndex); CurIndex+=1; if(CurIndex > 3) CurIndex = 0; } void CCWStep() { DoStep(CurIndex); CurIndex-=1; if(CurIndex < 0) CurIndex = 3; }
      
      





次に、エンジンを正しい方向に正しいステップ数だけ回転させる関数を作成します。

 void Steps(u8 dir, s16 n) { s16 i = 0; for(i = 0; i < n; i++) { if(dir) { CWStep(); udelay(15000); } else { CCWStep(); udelay(15000); } } }
      
      





この関数では、次のステップにコマンドを送信する前にステッピングモーターがステップを実行する時間があるように、時間遅延が挿入されます。



私たちの手の中の力はますます増えており、次の段階に進んでいます。データをPCに送信し、PCからSDを管理します。



USB通信



USBを使用するために、USBの使用例の1つ、つまりVirtualComport_Loopbackを使用しました(STM32 USB-FS-Device開発キットでインターネットを検索します)。 このデモでは、PCに接続されたstm32は仮想COMポートとして定義され、受信したすべてのデータを送り返しました。 まあ、それは私たちにぴったりです! この例を取り上げて、交換のループを破り、できればそれを使用します。



発生した唯一の問題は、マイクロプロセッサがセンサーを絶えずポーリングしてデータをPCに送信した場合、.Netアプリケーションが仮想COMポートに接続したくないということでした(デバッグに使用したサードパーティのHerculesプログラムがポートを完全に開いたのは興味深いことです)。 そのため、ユーザーボタンを押すまで待機することにしました。その後、センサーの常時調査とPCとの情報交換が既に開始されていました。



実際に、次のコードについて取得しました。

USB初期化:

  Set_System(); Set_USBClock(); USB_Interrupts_Config(); USB_Init();
      
      





ユーザーボタンを押すまで待機します。

  while (1) { /* Data exhange via USB */ if (bDeviceState == CONFIGURED && UserButtonPressed != 0) { … } }
      
      





UserButtonをクリックするためのハンドラー:

 __IO uint32_t i =0; extern __IO uint32_t UserButtonPressed; void EXTI0_IRQHandler(void) { if ((EXTI_GetITStatus(USER_BUTTON_EXTI_LINE) == SET)&&(STM_EVAL_PBGetState(BUTTON_USER) != RESET)) { /* Delay */ for(i=0; i<0x7FFFF; i++); /* Wait for SEL button to be pressed */ while(STM_EVAL_PBGetState(BUTTON_USER) != RESET); /* Delay */ for(i=0; i<0x7FFFF; i++); UserButtonPressed++; if (UserButtonPressed > 0x2) { UserButtonPressed = 0x0; } /* Clear the EXTI line pending bit */ EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE); } }
      
      







おわりに



この記事では、ピン配置とデバイスの相互接続、回路基板、その他の詳細(ADCの使用)の多くの側面を省略し、周辺機器の使用に焦点を当てようとしました。 残念ながら、組み立てられた作業レイアウトは大学に引き渡されました(将来の世代がこの作業に興味を持ち、それを継続することを期待しましょう)、その結果、私はその作業を実証することはできませんが、まだいくつかの写真があります。 たとえば、さまざまな空気懸架剛性を持つ正弦波表面で車両の物理モデルを移動するときの加速度振幅を決定する実験を行ったときの写真。





また、STM32F3の下でIARのプロジェクトを添付します。 「うまくいけば、しかしすぐに」という原則に基づいて大部分が記述されているため、多くの「悪い」コードがあります。 コードに関するコメントだけでなく、感謝します。



dl.dropboxusercontent.com/u/61862295/VirtualComport_Loopback.rar



大学に感謝の意を表したいと思います。大学は、6年以上にわたり多くのことを教えてくれました。卒業証書のリーダーは、適度に反応し、常に「科学」活動を助けてくれました。





使用されたソース:



1. STM32F3 Discovery kitファームウェアパッケージ。28のサンプルと4つの異なるIDE用の事前構成済みプロジェクトを含むwww.st.com/web/en/catalog/tools/PF258154

2.リストされたすべてのデバイスのDatashits。



All Articles