目錄
背景
?搖桿的原理
程序
端口配置
ADC 配置
DMA配置
背景
DMA是一種計算機技術,允許某些硬件子系統直接訪問系統內存,而不需要中央處理器(CPU)的介入,從而減輕CPU的負擔。我們可以通過DMA來從外設(ADC、UART等)讀取數據之后,搬運到指定的內存。
ADC是根據用戶動作或者環境變化會造成傳感器等設備的電壓值發生變化,再通過STM32的ADC塊實現采樣、保持、量化、編碼將模擬量轉換成數據量。
本篇文章會介紹目前無人機常用的搖桿操作(左右、上下、按下等),變為電壓值變化,以及STM32 如何實現DMA定期讀取遙感ADC值。
?搖桿的原理
JS_X:作為X軸方向的模擬信號輸入口
JS_Y:作為Y軸方向的模擬信號輸入口
JS_D:揮動開關的狀態的檢測端口
通過原理圖可以通過X軸方向滑動可以改變接觸點在電阻的位置,從而影響讀取到的電壓值。Y軸方向亦是同理。因此只需將JS_X和JS_Y的端口設置位模擬量輸入端,然后由STM32單片機的ADC塊處理。
而按下的操作顯然是普通的Port Key, 因此只用將JS_D設置位上拉輸入,然后通過讀取該端口的狀態,來判斷是否搖桿被按下。
?STM32的DMA通道?:STM32系列最多有12個獨立可配置的通道,包括DMA1(7個通道)和DMA2(5個通道)。每個通道可以分別設置源地址與目的地址,實現獨立工作?
程序
端口配置
#define ADC1_DR_Address ((uint32_t)0x4001244C) //ADC1這個外設的地址(查參考手冊得出)#define ADCPORT GPIOA //定義ADC接口
#define ADC_CH4 GPIO_Pin_4 //定義ADC接口 電壓電位器
#define ADC_CH5 GPIO_Pin_5 //定義ADC接口 光敏電阻
#define ADC_CH6 GPIO_Pin_6 //定義ADC接口 搖桿X軸
#define ADC_CH7 GPIO_Pin_7 //定義ADC接口 搖桿Y軸#define JoyStickPORT GPIOB //定義IO接口組
#define JoyStick_KEY GPIO_Pin_2 //定義IO接口void ADC_GPIO_Init(void){ //GPIO初始化設置GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA時鐘(用于ADC的數據傳送)RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1時鐘GPIO_InitStructure.GPIO_Pin = ADC_CH6 | ADC_CH7; //!!!選擇端口 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //選擇IO接口工作方式 GPIO_Init(ADCPORT, &GPIO_InitStructure);
}void JoyStick_Init(void){ //搖桿的揮動開關的接口初始化GPIO_InitTypeDef GPIO_InitStructure; //定義GPIO的初始化枚舉結構 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE); GPIO_InitStructure.GPIO_Pin = JoyStick_KEY; //選擇端口號(0~15或all) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //選擇IO接口工作方式 //上拉電阻 GPIO_Init(JoyStickPORT,&GPIO_InitStructure);
}
1)GPIOA組、GPIOB組、DMA1的外設時鐘使能起來(GPIOC組功能用于其他,我們可以忽略它)
2) X軸和Y軸的輸入端要設置為模擬量輸入端口
3)Port Key 要設置位上拉電阻輸入口,因為在沒有被按下時,要能讀入高電平!
3)初始化端口
ADC 配置
void ADC_Configuration(void){ //初始化設置ADC_InitTypeDef ADC_InitStructure;//定義ADC初始化結構體變量ADC_GPIO_Init();//GPIO初始化設置ADC_DMA_Init();//DMA初始化設置ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在獨立模式ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能掃描ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC轉換工作在連續模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有軟件控制轉換ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//轉換數據右對齊ADC_InitStructure.ADC_NbrOfChannel = 2;//!!!順序進行規則轉換的ADC輸入口的數目(根據ADC采集通道數量修改)ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器//設置指定ADC的規則組通道,設置它們的轉化順序和采樣時間//ADC1,ADC通道x,規則采樣順序值為y,采樣時間為28周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);//!!! ADC1選擇信道x,采樣順序y,采樣時間n個周期ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_28Cycles5);//!!! ADC1選擇信道x,采樣順序y,采樣時間n個周期ADC_DMACmd(ADC1, ENABLE);// 開啟ADC的DMA支持(要實現DMA功能,還需獨立配置DMA通道等參數)ADC_Cmd(ADC1, ENABLE);//使能ADC1ADC_ResetCalibration(ADC1); //重置ADC1校準寄存器while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校準重置完成ADC_StartCalibration(ADC1);//開始ADC1校準while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校準完成ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1軟件開始轉換
}





常見的ADC數據對齊方式
右對齊(默認情況):這是大多數STM32 ADC寄存器的默認行為。例如,如果你使用12位分辨率的ADC,那么只有最低的12位將被用來存儲轉換結果,而最高的4位(在16位寄存器中)將會是0。
左對齊:在某些情況下,你可能需要將數據左對齊,這意味著將最高有效位(MSB)放在寄存器的最高位。這通常通過特定的硬件配置或軟件操作來實現,比如在某些STM32系列中,可以通過配置ADC寄存器來實現數據的左對齊。
輸入通道數配置,X軸通道和Y軸通道總共兩個。
注冊規則組
注冊包含通道信息、采樣順序、采樣時間。ADC通道通過查閱端口定義可知。
STM32F103C8T6
C8:48pin 64K Flash?屬于中容量 SRAM 20K.
查看引腳定義可以知道PA6的ADC通道是6,PA7的通道是7.
使能ADC1的DMA請求
使能ADC1
ADC1自校準
軟件觸發開始ADC1轉換
DMA配置
vu16 ADC_DMA_IN[2]; //ADC數值存放的變量void ADC_DMA_Init(void){ //DMA初始化設置DMA_InitTypeDef DMA_InitStructure;//定義DMA初始化結構體DMA_DeInit(DMA1_Channel1);//復位DMA通道1DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定義 DMA通道外設基地址=ADC1_DR_AddressDMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定義DMA通道ADC數據存儲器(其他函數可直接讀此變量即是ADC值)DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外設為源地址DMA_InitStructure.DMA_BufferSize = 2;//!!!定義DMA緩沖區大小(根據ADC采集通道數量修改)DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//當前外設寄存器地址不變DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 當前存儲器地址:Disable不變,Enable遞增(用于多通道采集)DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定義外設數據寬度16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定義存儲器數據寬度16位DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位環形緩沖模式DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道優先級高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存儲器到存儲器傳輸DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
由于是從ADC1外設(ADC1的數據寄存器DR)到內存,因此方向配置如下
DMA設置為循環模式,可以連續多次的從ADC1外設搬運數據到指定內存。
因為ADC1外設(DR)是16位(ADC分辨率是12位),所以
DMA1的每個channel用于設置數據源外設和目的地內存地址。因為我們的外設是ADC1。所以我們選擇Channel1.
?
ADC1的外設的地址
DMA優先級設置為HIGH
優先級相同情況下,由內部硬件優先級決定!
由于不是內存搬運到內存,故DMA_M2M要設置為Disable