- 项目需求
- IO扩展板上的2个按键和旋转编码器的3个输入端口是通过R-2R电阻网络的方式连接在一起,生成一个模拟电压量。按下任何一个按键都会改变这个模拟电压量的值。
- IO扩展板上的LCD屏幕为128*128分辨率的1.44寸彩色屏幕,通过SPI总线进行访问
- 通过MSP430核心板的ADC监测IO板模拟输出管脚的变化,判断哪一个按键或编码器的旋转发生了变化,进而控制1.44寸LCD屏幕的菜单显示。
- 要求实现主菜单和至少二级菜单。
- 功能实现
2.1、LCD显示
MSP430的扩展板上搭载了1.44寸的SPI串口TFT彩屏st7735,控制LCD显示图片或者想要的文字。
通过对LCD与MSP430的八个引脚进行控制,从而显示图片以及字符。
2.2、ADC采集
MSP430上的ADC模块支持12位模数转换,具有12位逐次逼近的时序控制电路和16个转换结果缓冲及控制寄存器。
2.3、按键检测
扩展板上有两个独立按键和一个旋转编码器,通过ADC采集A_OUT(P6.0)的电压,检测它的变化,从而来判断哪个按键被按下以及编码器是否发生了旋转。
2.4、二级菜单与光标
MSP430上电之后,显示主菜单,分别有三个选项,进入任何一个选项就进入了二级菜单,光标的上下移动通过旋转编码器来控制。
- 实现思路
- LCD和ADC进行初始化,LCD显示图片和字符,ADC对模拟量进行采集,在ADC中断获取数字量。
- 对获取的ADC进行判断,从而确定哪个按键按下以及编码器的旋转。
- 两个按键分别对应进入和退出,旋转编码器控制光标的移动。
- 按下旋转编码器具有保存的功能,能够保存数字的大小。
- 二级菜单的三个界面分别对应着不同的功能。
4.实现过程
4.1、思路流程图
4.2、ADC检测
测量引脚开启复用输入,将P6.0复用输入,配置ADC模块,ADC模块时钟源选为MODCLK时钟,不分频。内部模块振荡器(MODOSC)能够产生以约4.8Mhz的MODCLK时钟。Flash控制器模块、ADC_12模块等片内外设都可使用MODCLK作为内部参考时钟
a.ADC初始化
//Initialize the ADC12_A Module
/*
* Base address of ADC12_A Module
* Use internal ADC12_A bit as sample/hold signal to start conversion
* USE MODOSC 5MHZ Digital Oscillator as clock source
* Use default clock divider of 1
*/
ADC12_A_init(ADC12_A_BASE,
ADC12_A_SAMPLEHOLDSOURCE_SC,
ADC12_A_CLOCKSOURCE_ADC12OSC,
ADC12_A_CLOCKDIVIDER_1);
ADC12_A_enable(ADC12_A_BASE);
b.配置采样定时器
/*
* Base address of ADC12_A Module
* For memory buffers 0-7 sample/hold for 64 clock cycles
* For memory buffers 8-15 sample/hold for 4 clock cycles (default)
* Disable Multiple Sampling
*/
ADC12_A_setupSamplingTimer(ADC12_A_BASE,
ADC12_A_CYCLEHOLD_64_CYCLES,
ADC12_A_CYCLEHOLD_4_CYCLES,
ADC12_A_MULTIPLESAMPLESDISABLE);
配置采样结果缓冲寄存器,选择输入信号为A0,正参考电压源选为+3.3V,负参考电压源选为0V。
//Configure Memory Buffer
/*
* Base address of the ADC12_A Module
* Configure memory buffer 0
* Map input A5 to memory buffer 0
* Vref+ = AVcc(+3.3V)
* Vr- = AVss(0V)
* Memory buffer 0 is not the end of a sequence
*/
ADC12_A_configureMemoryParam param = {0};
param.memoryBufferControlIndex = ADC12_A_MEMORY_0;
param.inputSourceSelect = ADC12_A_INPUT_A0;
param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC;
param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS;
param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE;
ADC12_A_configureMemory(ADC12_A_BASE ,¶m);
c.配置ADC中断
因为使用的是ADC12_A_MEMORY_0,故使能为Enable memory buffer 0 interrupt
//Enable memory buffer 0 interrupt
ADC12_A_clearInterrupt(ADC12_A_BASE,
ADC12IFG0);
ADC12_A_enableInterrupt(ADC12_A_BASE,
ADC12IE0);
d.启动ADC转换
while(1)
{
//Enable/Start sampling and conversion
/*
* Base address of ADC12_A Module
* Start the conversion into memory buffer 0
* Use the single-channel, single-conversion mode
*/
ADC12_A_startConversion(ADC12_A_BASE, ADC12_A_MEMORY_0, ADC12_A_SINGLECHANNEL);
//LPM0, ADC12_A_ISR will force exit
__bis_SR_register(LPM0_bits + GIE);
//for Debugger
__no_operation();
}
4.3、按键检测
a.界面选择
设置flag_page_1为界面标志位,当标志位分别为1、11、12、13时,分别对应主界面、二级界面一、二级界面二、二级界面三。
switch(flag_page_1) //选择界面
{
case 1:
LCD_Page_1();
break;
case 11:
LCD_Page_11();
break;
case 12:
LCD_Page_12();
break;
case 13 :
LCD_Page_13();
break;
default: break;
}
b、按键检测
通过判断P6.0引脚的ADC模拟量变化来确定是哪个按键被按下。
void key_scan(void)
{
if(keyvalue)
keyvalue=0;
if ( Measured>3500&& Measured<3700)
{
if((float)Measured/0xfff*3.3>2.85&&(float)Measured/0xfff*3.3<3.1)
keyvalue = 1;
}
else if ( Measured>2900&& Measured<3000)
{
keyvalue = 2;
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
}
else if ( Measured>1900&& Measured<2000)
{
keyvalue = 3;
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
}
else if( Measured>3400&& Measured<3500)
{
keyvalue = 4;
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
}
}
c.按键服务函数
不同的按键有不同的功能,当对应的按键被按下,就会进入到按键相应的功能区域。
void key_service(void)
{
switch(keyvalue)
{
case 0:
break;
case 1:
{
flag_page_1_++;
if(flag_page_1_==4)
flag_page_1_=1;
}
break;
case 2:
{
switch(flag_page_1_)
{
case 1:
flag_page_1=11;
break;
case 2:
flag_page_1=12;
break;
case 3:
flag_page_1=13;
break;
}
break;
}
case 3:
flag_page_1=1;
break;
case 4:
if(flag_page_1==11)
p=num;
break;
default: break;
}
}
d.光标移动
设置光标标志位,当检测到编码器旋转后,标志位加一,以三为一个循环。
switch(flag_page_1_)
{
case 1:
LCD_ShowString(0,24," ->",BLUE, WHITE,16,0);
break;
case 2:
LCD_ShowString(0,48," ->",BLUE, WHITE,16,0);
break;
case 3:
LCD_ShowString(0,72," ->",BLUE, WHITE,16,0);
break;
}
5.遇到的主要问题
5.1、ADC滤波
在使用ADC时,不断地对P6.0引脚的电压进行采集,但是在采集的过程中,由于采用的是单通道的ADDC进行采集,得到的数据并不稳定,精度也并不高。因此在对编码器进行旋转判断时不是很准确,也就是检测并没有很灵敏。虽然能判断独立按键按下,但对旋转编码器的带电压变化捕捉不够理想。并且当编码器旋转到正好将两个小按键按下时,光标会不断上下移动,当编码器离开那个位置后,又恢复正常。
若对ADC检测到的信号进行滤波,首先能滤除杂波,使得信号更加平稳,所得到的误差也会减小。
5.2、LCD刷新
每进入一个新界面就刷新一次会造成界面重复刷新,因此,将LCD清屏函数放在按键扫描之后,每次检测到按键被按下之后,就将LCD清屏一次,这样就解决了重复刷新清屏问题。
6、未来规划与收获
本次项目成功基本实现了简易的二级菜单功能,但还是有很多的不足和可以改进的地方:
- ADC的检测还可以进一步升级和完善,使之检测更加灵敏。
- 还可以设置3级或者更多级的菜单,不同的菜单功能也可以更加多样化。
- 旋转编码器能够判断出顺、逆时针方向的旋转。
- 设置图案化的菜单界面,按键具有双击、长短按的功能。
经过这次寒假一起练平台,学到了很多东西:
- 了解并初步学习了MSP430单片机的一些相关内容,对单片机的认识有了进一步的了解。
- 尝试用ADC来检测按键按下和编码器的旋转,与以往对按键的检测通过I/O口不同。
- 使用MSP430驱动LCD屏来显示图片和字符。
- 和志同道合的小伙伴一起相互学习,解决问题。