STM32シリアルポートアイドル割り込み+ DMAがMPU6050データを読み取る
STM32シリアルポートアイドル割り込み
データを受信するプロセス:
DMAを受信するシリアルポートは、初期化されるとオン状態になり、データが到着するまで待機します。初期構成中に構成が設定されている限り、ソフトウェアで何もする必要はありません。
データデータ受信完了
の判定:ここで受信完了の判定は、シリアルポートのアイドル割り込みにより行われます。つまり、シリアルデータの流れが停止するとIDLE割り込みが発生します。この割り込みでは、次のことが行われます。
- DMAチャネルを受信するシリアルポートを閉じるには、次の2つの理由があります:a。後続のデータ受信を防ぎ、干渉を引き起こします。b。4番目のポイントであるDMAの再構成と割り当てを容易にします。
- 受信完了フラグを立てる
- 受信バッファー内のデータを処理する
- 次にDMAが受信するデータバイト数をリセットします。ここでは、DMAレジスタの受信カウント値をリセットします。この数は、受信可能なバイト数以上でなければなりません。それ以外の場合、DMA受信カウンターが0にデクリメントするときそのとき、カウント値が再度リロードされ、カウントが再循環されるため、受信バッファー内のデータは上書きされて失われます。
- DMAチャネルをオンにして、次のデータ受信を待ちます。DMA関連のレジスタ構成への書き込み(第4条の書き込みカウント値など)は、DMAをオフにした状態で実行する必要があります。
STM32のIDLE割り込みは、シリアルポートでデータ受信がない場合は常に生成されるわけではなく、IDLEフラグビットがクリアされている場合は、最初のデータを受信してから開始する必要があることを説明します。トリガーされ、受信データが中断され、データが受信されなくなると、IDLE割り込みが生成されます。IDLEビットは、RXNEビットが設定される(つまり、アイドルバスが再度検出される)まで、再度Highに設定されることはありません。RXNE受信割り込みをオンにできないため、割り込み数が減少します。
void My_UART_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 使能DMA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // 使能时钟 复用USART
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //初始化 USART_TX 即 GPIOA.9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct); //初始化 USART_RX 即 GPIOA.10
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStruct); //初始化 USART
// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启 USART 接收缓冲区非空中断
// USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 开启 USART 发送缓冲区空中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //开启 USART1 总线空闲中断
USART_Cmd(USART1, ENABLE);//使能USART中断
DMA_DeInit(DMA1_Channel5);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR); //外设--->内存
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)RxBuffer;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_BufferSize = BufferSize;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_Init(DMA1_Channel5, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel5, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); // 使能 USART1接收DMA
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级 2位 00 01 10 11
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级 2位 00 01 10 11
NVIC_Init(&NVIC_InitStruct); //初始化中断
}
MCUがUSARTを介して外部から送信されたデータを受信すると、メインプログラムは手順①②③を無視でき、DMAは受信データをバッファRxBufferに直接書き込みます。このとき、プログラムは受信割り込みに入りません。受信が完了すると、受信アイドル割り込みが生成され、割り込みサービス関数で受信完了フラグが1に設定され、受信バッファー内のデータ長が計算され、割り込みビットがクリアされ、DMAが無効になり、データの処理時にデータの受信を防止します。メインプログラムで受信完了フラグが1に設定されていることが検出され、データ処理プログラムに入り、受信完了フラグが0に設定され、次にDMAが受信するデータバイト数をリセットし、DMAが受信データ状態になるようにします。
void USART1_IRQHandler(void)
{
uint8_t clear = clear; // 用来消除编译器的“没有用到”的提醒
uint8_t data = 0;
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
{
clear = USART1->SR;
clear = USART1->DR;
// RxCounter = BufferSize - DMA1_Channel5->CNDTR;//缓存中的字节数
RxCounter = BufferSize - DMA_GetCurrDataCounter(DMA1_Channel5);//缓存中的字节数
// USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
RxStatus = 1; //标记接收到一帧
USART_ClearITPendingBit(USART1, USART_IT_IDLE); // 清除空闲中断
DMA_Cmd(DMA1_Channel5, DISABLE); // 停止DMA,清除DMA缓存
}
}
int main(void)
{
uint8_t i = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 初始化中断优先级分组
My_UART_Init();
while(1)
{
if(RxStatus == 1)
{
RxStatus = 0;
i = 0;
while(RxCounter--)
{
USART_SendData(USART1, RxBuffer[i++]);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);
}
memset(RxBuffer, 0, i); // 清除缓存
RxCounter = 0;
// DMA1_Channel5->CNDTR = BufferSize;
DMA_SetCurrDataCounter(DMA1_Channel5, BufferSize);
DMA_Cmd(DMA1_Channel5, ENABLE);
}
}
}
DMAの紹介
DMAの定義
DMA(ダイレクトメモリアクセス、DMA)は、コンピュータサイエンスにおけるメモリアクセス技術です。これにより、特定のコンピューターの内部ハードウェアサブシステム(コンピューター周辺機器)が、CPUをバイパスせずにシステムメモリを個別に読み書きできます。同じ程度のCPU負荷の下で、DMAは高速データ転送方式です。これにより、CPUからの多数の割り込み要求に依存することなく、さまざまな速度のハードウェアデバイスが通信できます。
DMAの用途は何ですか?
ダイレクトメモリアクセスは、周辺機器とメモリ間、またはメモリとメモリ間の高速データ転送を提供するために使用されます。CPUの介入がなくても、DMAを介してデータをすばやく移動できます。これにより、他の操作のためのCPUリソースが節約されます。
DMAリソースはいくつありますか?
DMAコントローラは2つあり、DMA1には7チャネル、DMA2には5チャネルがあります。
データはどこから送信されますか?
- 周辺機器はSRAMに行き(I2C / UARTなどでデータを取得し、SRAMに送信します)、SRAM
の2つの領域間を移動します。 - 周辺機器から周辺機器へ(ADCはデータを読み取り、TIM1に送信して、さまざまなPWMデューティサイクルを生成するように制御します);
- SRAMからペリフェラル(SRAMに保存されたデータはDACに送信され、さまざまな波形を生成します);
DMA転送できるデータ量はどれくらいですか?
DMAの従来の概念は大量のデータの送信に使用されますが、STM32ではその概念が拡張されており、多くの場合、高速がそのアプリケーションの焦点であると理解しています。データは1〜65535です。
DMAコントローラーとアービター
現在、ますます多くのシングルチップマイクロコンピュータがDMA技術を採用して、周辺機器とメモリ間またはメモリ間で高速データ転送を提供しています。CPUがこの転送アクションを開始すると、転送アクション自体はDMAコントローラ実装して完了する。STM32にはDMAコントローラがあり、7つのチャネルがあり、各チャネルはメモリアクセスを要求する1つ以上のペリフェラルの管理専用で、アービタ各DMA要求の優先順位を調整します。
DMAコントローラとCortex-M3コアは、システムデータバスを共有して、直接メモリデータ転送を実行します。CPUとDMAが同じターゲット(RAMまたは周辺機器)に同時にアクセスすると、DMA要求によりCPUがシステムバスにアクセスするのを数サイクル停止し、バスアービターが循環スケジューリングを実行して、CPUがシステムバス(メモリ)の少なくとも半分を取得できるようにします。または周辺機器)帯域幅。
イベントが発生すると、ペリフェラルはリクエスト信号をDMAコントローラに送信します。DMAコントローラは、チャネルの優先度に従って要求を処理します。DMAコントローラがペリフェラルへのアクセスを開始すると、DMAコントローラは直ちにペリフェラルに応答信号を送信します。DMAコントローラから応答信号を受信すると、ペリフェラルはその要求をすぐに解放します。ペリフェラルがこの要求を解放すると、DMAコントローラは同時に応答信号をキャンセルします。さらに要求が発生した場合、ペリフェラルは次の処理を開始できます。
コード表示
シリアルポートアイドル+ HALライブラリに基づくDMAメイン受信コード
以下はヘッダーファイルです
#ifndef DMAT_H
#define DMAT_H
#include "stm32f1xx_hal.h"
#include "usart.h"
extern uint8_t usart2_rx_buffer[33];
extern uint8_t usart3_rx_buffer[25];
void MyU2Init(void);
void MyU3Init(void);
void USART2Handle(void);
void USART3Handle(void);
void MyUsartHandle(UART_HandleTypeDef *HUART);
#endif
以下はソースファイルです
#include "dmat.h"
uint8_t usart2_rx_buffer[33]={
0};
uint8_t usart3_rx_buffer[25]={
0};
float ROLL_ANGLE=0;
float PITCH_ANGLE=0;
float YAW_ANGLE=0;
void MyU2Init(void)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart2,usart2_rx_buffer,33);
}
void MyU3Init(void)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart3,usart3_rx_buffer,25);
}
//可以在里面加上想要的功能
//以及数据处理的代码
//这是采用串口+DMA+空闲中断来处理串口接收的数据
void USART2Handle(void)
{
if(usart2_rx_buffer[0]==0x55&&usart2_rx_buffer[11]==0x55&&usart2_rx_buffer[22]==0x55)
{
//总共33个字节的数据,每11个字节的数据算一个数据包,三个数据包分别是加速度包,角速度包和角度包
if(usart2_rx_buffer[23]==0x53)
{
//下面的数据是针对角度包来解析的,得到三个角度
ROLL_ANGLE=((int16_t)(usart2_rx_buffer[25]<<8|usart2_rx_buffer[24]))/32768.0*180;
PITCH_ANGLE=((int16_t)(usart2_rx_buffer[27]<<8|usart2_rx_buffer[26]))/32768.0*180;
YAW_ANGLE=((int16_t)(usart2_rx_buffer[29]<<8|usart2_rx_buffer[28]))/32768.0*180;
}
}
}
void USART3Handle(void)
{
}
void MyUsartHandle(UART_HandleTypeDef *HUART)
{
if(HUART==&huart2)
{
if(__HAL_UART_GET_FLAG(HUART,UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(HUART);
HAL_UART_DMAStop(HUART);
USART2Handle();
HAL_UART_Receive_DMA(HUART,(uint8_t*) usart2_rx_buffer,33);
}
}
else if(HUART==&huart3)
{
if(__HAL_UART_GET_FLAG(HUART,UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(HUART);
HAL_UART_DMAStop(HUART);
USART3Handle();
HAL_UART_Receive_DMA(HUART,(uint8_t*) usart2_rx_buffer,25);
}
}
}