雙軸按鍵搖桿控制器控制TFTLCD(使用ADC1雙通道DMA傳輸)


實驗使用如下所示的雙軸按鍵搖桿控制器,來控制TFTLCD上顯示的直線。首先介紹一下雙軸按鍵搖桿控制器。原理:十字搖桿為一個雙向的10K電阻器,隨着搖桿方向不同,抽頭的阻值隨着變化。本模塊使用5V供電在本實驗中使用3.3V,原始狀態下X,Y讀出電壓為2.5V左右(本實驗為1.65V),當隨箭頭方向按下,讀出電壓值隨着增加,最大到5V(本實驗最大為3.3V);箭頭相反方向按下,讀出電壓值減少,最小為0V。即模塊特設二路模擬輸出和一路數字輸出接口,輸出值分別對應(X,Y)雙軸偏移量,其類型為模擬量;按鍵表示用戶是否在Z軸上按下,其類型為數字開關量。坐標標識符清晰簡明、准確定位;用其可以輕松控制物體(如二自由度舵機雲台)在二維空間運動。

實驗目的:

在屏幕的中心區域顯示一條射線,射線起點為屏幕中心(120,160),射線方向與搖桿歪的方向相同,射線長度與歪的程度有關。線路連接:

PC1 - ADC1 channel_11;
PC0 - ADC1 channel_10;
PC2 - SW;

實驗准備:

1、實驗中對搖桿兩個模擬段輸入的檢測需要使用STM32 的ADC功能;

2、在數據轉換之后的移動數據時使用DMA,以將數據及時轉移出ADC的寄存器;

我們先來看看主函數,在主函數中我們定義了浮點型數組float ADC_ConvertedValueLocal[2];用於保存轉換計算后的電壓值 ,還有在adc.c文件中定義的數組ADC_ConvertedValue[2];用來裝轉換后的數據。

#include <stdio.h>
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "usart.h"
#include "adc.h"
#include "lcd.h"

// ADC1轉換的電壓值通過MDA方式傳到SRAM
extern __IO uint16_t ADC_ConvertedValue[2];

// 局部變量,用於保存轉換計算后的電壓值             
float ADC_ConvertedValueLocal[2];    

int main(void)
{
  u8 x=0;
  u8 lcd_id[12];            //存放LCD ID字符串
  SysTick_Init();//延時初始化
  USART1_Int(9600);
  LCD_Init();
  ADC1_Init();
 POINT_COLOR=RED;
  while(1) 
    {        
        
        if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2)!=1){
            LCD_Clear( YELLOW);
            LCD_ShowString(20,100,200,100,16, "THANKS TO YOU!");    
            Delay_ms(2000);    
            LCD_Clear( WHITE);
        }
        
        for(x=0;x<2;x++){
            ADC_ConvertedValueLocal[x] =(float) ADC_ConvertedValue[x]/4096*3.3; // 讀取轉換的AD值
            LCD_ShowxNum(0,0+x*20,ADC_ConvertedValue[x],5,16,0);
        }

        LCD_DrawLine(124, 158, 165-(50*ADC_ConvertedValueLocal[1])+42 , 50*ADC_ConvertedValueLocal[0]+78);                
           
        Delay_ms(100);    
        LCD_Fill(34,72,210,246,LGRAY);
        
    } 
}

在主函數的while(1)循環之前,我進了三個初始化,分別是:

  USART1_Int(9600);
  LCD_Init();
  ADC1_Init();

在這里第一個就不在介紹,不了解的可以參考:http://www.ciast.net/post/2015119.html 。第二個在這里也不作為重點介紹,TFTLCD的介紹可以參見 http://www.ciast.net/post/20151112.html ,在這里我們會使用到lcd.c中定義的一些操作液晶屏的函數,如下所示:

//清屏函數
//color:要清屏的填充色
void LCD_Clear(u16 color);
//在指定區域內填充單個顏色
//(sx,sy),(ex,ey):填充矩形對角坐標,區域大小為:(ex-sx+1)*(ey-sy+1)   
//color:要填充的顏色
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);
//畫線
//x1,y1:起點坐標
//x2,y2:終點坐標  
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
//顯示數字,高位為0,還是顯示
//x,y:起點坐標
//num:數值(0~999999999);     
//len:長度(即要顯示的位數)
//size:字體大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非疊加顯示;1,疊加顯示.
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);
//顯示字符串
//x,y:起點坐標
//width,height:區域大小  
//size:字體大小
//*p:字符串起始地址          
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)

這些功能將在while(1)循環中使用。我們現在着重要將的是ADC1_Init();這個初始化函數,我們找到這個函數的定義,內容如下:

/*
 * 函數名:ADC1_Init
 * 描述  :無
 * 輸入  :無
 * 輸出  :無
 * 調用  :外部調用
 */
void ADC1_Init(void)
{
    ADC1_GPIO_Config();
    ADC1_Mode_Config();
}

這個函數由另外兩個函數組成,分別是:

/*
 * 函數名:ADC1_GPIO_Config
 * 描述  :使能ADC1和DMA1的時鍾,初始化PC.00、PC.01和PC.02
 * 輸入  : 無
 * 輸出  :無
 * 調用  :內部調用
 */
static void ADC1_GPIO_Config(void);
/* 函數名:ADC1_Mode_Config
 * 描述  :配置ADC1的工作模式為MDA模式
 * 輸入  : 無
 * 輸出  :無
 * 調用  :內部調用
 */
static void ADC1_Mode_Config(void);

首先看的是第一個函數,即引腳定義:

/*
 * 函數名:ADC1_GPIO_Config
 * 描述  :使能ADC1和DMA1的時鍾,初始化PC.01
 * 輸入  : 無
 * 輸出  :無
 * 調用  :內部調用
 */
static void ADC1_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* Enable DMA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    /* Enable ADC1 and GPIOC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
    
    /* Configure PC.01  as analog input */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);                // PC1 PC0,輸入時不用設置速率
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);                // PC2 設置按鍵功能
}

我將兩個模擬輸入設置為GPIO_Mode_AIN(模擬輸入),將PC2 按鍵功能鍵設置成上拉輸入(搖桿的SW引腳已經被上拉)。下面是重點:

在ADC1_GPIO_Config(void)函數中,我們主要進行的是DMA和ADC1雙通道的設置,下面我們對負責傳輸的DMA進行設置:

DMA_InitTypeDef DMA_InitStructure;

/* DMA channel1 configuration */
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;     //ADC地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;//內存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 2;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設地址固定
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //內存地址固定
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;    //半字
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;        //循環傳輸
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    /* Enable DMA channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);

 

下面我們開始使用ADC1的通道10和通道11來轉換接受到的虛擬信號:

ADC_InitTypeDef ADC_InitStructure;

/* ADC1 configuration */
    
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;    //獨立ADC模式
    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;         //要轉換的通道數目2
    ADC_Init(ADC1, &ADC_InitStructure);
    
    /*配置ADC時鍾,為PCLK2的8分頻,即9Hz*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div8); 
    /*配置ADC1的通道11為55.    5個采樣周期,序列為1 */ 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_55Cycles5 );//ADC1;ADC1通道0;第2轉換;采樣時間為239.5周期

    
    ADC_DMACmd(ADC1, ENABLE);      /* Enable ADC1 DMA */
    ADC_Cmd(ADC1, ENABLE);          /* Enable ADC1 */
    ADC_ResetCalibration(ADC1);       /*復位校准寄存器 */
    while(ADC_GetResetCalibrationStatus(ADC1));     /*等待校准寄存器復位完成 */
    ADC_StartCalibration(ADC1);       /* ADC校准 */
    while(ADC_GetCalibrationStatus(ADC1));      /* 等待校准完成*/
    
    
    /* 由於沒有采用外部觸發,所以使用軟件觸發ADC轉換 */ 
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

我把整個ADC的設置分成了四個部分:

第一部分:主要是ADC的初始化參數配置函數,具體設置可以參考:http://www.ciast.net/post/20151226.html ,在這里使用獨立ADC模式模式,由於使用了雙通道,所以ADC_ScanConvMode = ENABLE ;即開啟掃描模式,且ADC_NbrOfChannel = 2;

第二部分:主要是配置ADC時鍾和規則通道設置。這里由於ADC的頻率不能大於12MHz,所以我們選擇分頻為PCLK2的8分頻,即9Hz。規則通道的設置是每一個通道都要進行的,可以設置通道和掃描順序以及掃描周期等,這也是ADC設置的重點,可以參考 http://www.ciast.net/post/20151226.html 。

第三部分:主要是使能DMA和ADC,以及復位校准;

第四部分:使用軟件觸發ADC轉換,轉換開始

至此初始化函數介紹完畢,下面開始while(1)循環部分的介紹:

  while(1) 
    {        
        if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2)!=1){
            LCD_Clear( YELLOW);
            LCD_ShowString(20,100,200,100,16, "THANKS TO YOU!");    
            Delay_ms(2000);    
            LCD_Clear( WHITE);
        }
        
        for(x=0;x<2;x++){
            ADC_ConvertedValueLocal[x] =(float) ADC_ConvertedValue[x]/4096*3.3; // 讀取轉換的AD值
            LCD_ShowxNum(0,0+x*20,ADC_ConvertedValue[x],5,16,0);
        }

        LCD_DrawLine(124, 158, 165-(50*ADC_ConvertedValueLocal[1])+42 , 50*ADC_ConvertedValueLocal[0]+78);                
                  
        Delay_ms(100);    
        LCD_Fill(34,72,210,246,LGRAY);
    } 

其中的重點是FOR語句:

        for(x=0;x<2;x++){
            ADC_ConvertedValueLocal[x] =(float) ADC_ConvertedValue[x]/4096*3.3; // 讀取轉換的AD值
            LCD_ShowxNum(0,0+x*20,ADC_ConvertedValue[x],5,16,0);
        }

在這個循環語句中,我們讀取兩個通道中的數據。其中ADC_ConvertedValue[x]為我們在一開始就定義的用來存數處理過的數據的數組,其中X取1和2。為什么這個數組可以接受到兩個通道的數據呢?其實在DMA中我們設置了數據的傳輸:

    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;     //ADC地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;//內存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

其中 傳輸方向為從外設到內存,而內存地址,我們取得就是這個數組的首地址,這樣轉換過的數據自然就被傳到了這個數組中。那么傳輸之前數據的處理又是怎么進行的呢?從下面的程序可以看到,我們在ADC設置時,使能了掃描和連續轉換,且通道數目為2,那么在連續轉換時,ADC就會按照下面ADC_RegularChannelConfig中設置的轉換順序進行連續的轉換所有的通道(即轉換完通道1后轉換通道2)。每次轉換完一個通道后,轉換好的數據就會被DMA轉走,從而回到了上面的步驟。

    ADC_InitStructure.ADC_ScanConvMode =     ENABLE ;      //禁止掃描模式,掃描模式用於多通道采集
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    //開啟連續轉換模式,即不停地進行ADC轉換
    ADC_InitStructure.ADC_NbrOfChannel = 2;         //要轉換的通道數目2
----------------------------------------------------------------------------------------
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_55Cycles5 );//ADC1;ADC1通道0;第2轉換;采樣時間為239.5周期

下面的語句是FOR語句下面的,實現的是話射線功能,我們使用的屏是240*320的,圖形具體尺寸見下圖:

LCD_DrawLine(124, 158, 165-(50*ADC_ConvertedValueLocal[1])+42 , 50*ADC_ConvertedValueLocal[0]+78);                
                  
Delay_ms(100);    
LCD_Fill(34,72,210,246,LGRAY);

中間一點是理論中心點(120,160),但是由於搖桿的電壓不是太穩定,出現了電壓波動,所示實際中心點是(124,159),中間的正方形是射線的邊界。

還有最后的IF語句,設置的是按下是的操作,實現的是按下后在屏幕上刷黃色屏,出現一個字符串:

if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2)!=1){
     LCD_Clear( YELLOW);
     LCD_ShowString(20,100,200,100,16, "THANKS TO YOU!");    
     Delay_ms(2000);    
     LCD_Clear( WHITE);
 }

現在整個實驗就結束了。這個實驗今天從早上7點多開始,我一直弄到下午4點半才成功,中間還百度了很多資料。

最終效果如下:

 

[完] 選自:http://www.ciast.net/post/20151227.html    CIAST.NET  @zgc261


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM