实验使用如下所示的双轴按键摇杆控制器,来控制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;         //要转换的通道数目2ADC_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

转载于:https://www.cnblogs.com/zitech/p/5033851.html

双轴按键摇杆控制器控制TFTLCD(使用ADC1双通道DMA传输)相关推荐

  1. Arduino提高篇12—双轴按键摇杆

    摇杆一般在航模.电玩.遥控车.云台等设备上应用广泛,很多带有屏幕的设备也经常使用摇杆作为菜单选择的输入控制.本篇介绍双轴按键摇杆的使用. 1. 双轴按键摇杆介绍 双轴按键摇杆主要由两个10K的电位器和 ...

  2. Arduino双轴按键摇杆

    双轴按键摇杆主要由两个10K的电位器和一个按键开关组成,两个电位器随着摇杆扭转角度分别输出X.Y轴上对应的电压值,在Z轴方向上按下摇杆可触发轻触按键.在配套机械结构的作用下,无外力扭动的摇杆初始状态下 ...

  3. 基于STM32的双轴XY摇杆传感器模块使用

    文章目录 前言 一.双轴XY摇杆传感器模块介绍 二.如何使用该模块 1.进行ADC转换 2.传输转换完成的数据(DMA) 3.串口打印 三.实现步骤 1.ADC初始化与配置参数 2.实现DMA传输 3 ...

  4. 六轴机械臂控制器 控制卡 软件 机械臂

    六轴机械臂控制器 控制卡 软件 机械臂27400624681113128教学实训设备

  5. 双轴机械臂串口控制命令开发与测试:STM32F103控制板,简易调试命令集合

    ▌01 底层串口控制命令 1.调试说明 本文是继 调试机械臂一体化控制电路 博文中对于 两轴机械臂+机械爪整体控制板设计与机械爪控制调试 在 基于STM32F103双轴机械臂完整电路板 控制下进行串口 ...

  6. STM32双轴摇杆控制空心杯电机

    STM32双轴摇杆控制空心杯电机 双轴摇杆传感器   PS2双轴按键游戏摇杆模块采用PS2游戏手柄上金属按键摇杆电位器.模块特设二路模拟输出和一路数字输出接口.输出值分别对应(×.Y双轴偏移量.其类型 ...

  7. 「雕爷学编程」Arduino动手做(38)——joystick双轴摇杆模块

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  8. 传感器自学笔记第十六章——双轴摇杆模块

    作者:GWD 时间:2019.7.1 一. 学习要点 1.检测电压常态时2.5V,最大值5V,最小值0V,实际上就是两个x,y方向上的滑动变阻器,用PCF8591模数转换模块的两个通道分别检测电压值的 ...

  9. 西门子S7-1200PLC双轴定位算法电池焊接控制博图程序案例,触摸屏画面采用威纶触摸屏

    西门子S7-1200PLC双轴定位算法电池焊接控制博图程序案例,触摸屏画面采用威纶触摸屏. 程序设计结构灵活,采用SCL语言+梯形图结构,项目包括: 1.博图V15PLC程序 2.威纶通触摸屏程序 3 ...

  10. LabVIEW编程LabVIEW开发 控制双轴倾角传感器BWH526例程与相关资料

    LabVIEW编程LabVIEW开发 控制双轴倾角传感器BWH526例程与相关资料 BWH526是北微传感推出的一款采用MEMS技术,数字输出的双轴高精度倾角传感器,测量范围±30°,全量程精度高达0 ...

最新文章

  1. 输入法项目-用delphi生成GBK 中文编码 GBK 扩充汉字编码表(3) GBK/3: $8140 —$A0FE(部分)...
  2. WPF MVVM从入门到精通1:MVVM模式简介
  3. Unity应用架构设计(7)——IoC工厂理念先行
  4. 复习支持向量机(SVM)没空看书时,掌握下面的知识就够了
  5. C#3种常见的定时器(多线程)
  6. 【最小生成树】路线规划(nowcoder 217603)
  7. java 设计模式 示例_Java中的中介器设计模式-示例教程
  8. Spring MVC + Hibernate + Maven:CRUD操作示例
  9. Go语言实现HashSet
  10. 原子性、有序性和可见性解释
  11. MYSQL集群的备份与恢复最终版本
  12. English Note_1_传统学习英语的误区
  13. halcon学习之边缘检测
  14. 【Oracle教程资源大合集】Oracle数据库免费学习资源汇总
  15. STM32用热敏电阻测温
  16. Redis解决高并发问题
  17. css同时多个立方体旋转,CSS3 旋转立方体问题详解
  18. 网络编程(6)--------javaweb(TCP协议)
  19. java长方体的父类_一个长方形类Rectangle,一个子类长方体类,计算周长,面积,体积...
  20. 来自CSDN的精华网址

热门文章

  1. anaconda装完没反应
  2. tensor.detach() 和 tensor.data 的区别
  3. 机器学习---分类、回归、聚类、降维的区别
  4. 这5小段代码轻松实现数据可视化(Python+Matplotlib)
  5. Python获取二维矩阵每列最大值
  6. 2021-06-19表单,内嵌框架
  7. kubernetes视频教程笔记 (20)-ingress
  8. docker用gpu的参数_Docker化部署(GPU)
  9. 神界计算机丢失msvcp120.dll,win8 msvcp120.dll丢失怎样修复?计算机中丢失msvcp120.dll处理办法...
  10. Unity3D(二)游戏对象及组件