一、项目需求
- IO扩展板上的2个按键和旋转编码器的3个输入端口是通过R-2R电阻网络的方式连接在一起,生成一个模拟电压量。按下任何一个按键都会改变这个模拟电压量的值。
- IO扩展板上的LCD屏幕为128*128分辨率的1.44寸彩色屏幕,通过SPI总线进行访问。
要求:本任务需要通过MSP430核心板的ADC监测IO板模拟输出管脚的变化,判断哪一个按键或编码器的旋转发生了变化,进而控制1.44寸LCD屏幕的菜单显示,要求实现主菜单和至少二级菜单。
二、完成的功能及达到的性能
1.项目介绍
上电后,出现”屏保“,按下确定键进入主菜单。
主菜单如图所示,主题为“我的实验室”,通过旋转编码器中的旋钮选择进入该主题的三个部分。
第一部分为团队,团队数量范围为0~5;可通过旋转编码器调整数值大小,按压旋转编码器为保存。
第二部分为新成员,数量范围为0~10;可通过旋转编码器调整数值大小,按压旋转编码器为保存。
第三部分为所获荣誉,数量范围为0~10。可通过旋转编码器调整数值大小,按压旋转编码器为保存。
2.操作功能
第一界面:可通过确定键进入第二界面。
第二界面:可通过退出键来到第一界面。用“->”代表选择的菜单,通过旋钮控制其在哪个菜单停留,确定键进入对应的界面。
第三界面:可通过退出键来到第二界面。通过旋转编码器调整数值大小,按压旋转编码器中央键可保存数值,未保存的数值再次进入时会丢失。
三、设计思路
- ADC对模拟输入进行采样,由定时器采样,软件触发;
- 将采样得到的ADC量化值显示在LCD上;
- 按下按键观察量化值,通过试验得出三个按键及旋钮的电压变化规律;
- 通过观察得到的规律,确定范围,实现各按键及旋钮的功能
四、实现思路
1.流程图
2.硬件介绍
如图所示,IO扩展板上的2个按键和旋转编码器的3个输入端口是通过R-2R电阻网络的方式连接在一起的,可以生成一个模拟电压量。按下任何一个按键都会改变这个模拟电压量的值,通过模拟电压量的变化使用其功能。
通过仿真及电路计算可知,当K2按下时,模拟电压量会变化1/2;K1按下时会变化1/4;旋钮上方按键按下时会改变1/8;旋钮BC处按压时会改变1/16;旋钮AC处按压时会改变1/32。从而判断按键状态。
IO扩展板上的LCD屏幕为128*128分辨率的1.44寸彩色屏幕,可以通过SPI总线进行访问。
五、实现过程
1.修改主频为25Mhz
430 默认情况下XT2是关闭的,系统的核心电压是一个比较低的值,在设置高频率,并且使用外部高速时钟之前我们需要打开时钟,并且在这之前我们需要把系统的核心电压升到最高。
430为了省电默认状态下核心电压默认设置为1.8V来节省功耗。 在用户手册的2.2.4节提到了升核心电压的方法.第一步是解锁 向PMMCTL0 寄存器高8位写入 0xA5 然后设置核心电压等级是三级(官方手册有提到频率和核心电压的关系)
然后令 SR寄存器里面的 SCG0=1,关闭锁相环,同时 关闭了这个时钟以后系统时钟应该会自动切换到备用时钟。再i选择 XTAL2 的时钟信号作为参考信号 并且分频到1MHz,逐步配置。
clock_init(25);//25Mhz
void clock_init(unsigned char Fre)//修改主频程序
{
P5SEL |= BIT2|BIT3|BIT4|BIT5;//开启外部两个时钟
UCSCTL6 |= XCAP_3|XT1OFF; // XT1 相关 配置
UCSCTL6 |= XT2DRIVE_0 |XT2OFF; // XT2 相关 配置
//以下是提升核心电压部分的代码
PMMCTL0_H = 0xA5; //开PMM电源管理
SVSMLCTL |= SVSMLRRL_1 + SVMLE; //配置SVML电压
PMMCTL0 = PMMPW + PMMCOREV_3; //配置内核电压
while((PMMIFG & SVSMLDLYIFG ) == 0); //等待设置完成
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG + SVSMLDLYIFG);
if((PMMIFG & SVMLIFG) == 1) //判断内核电压是否上升到VSVML
while((PMMIFG & SVMLVLRIFG) == 0); //如果没有等待
SVSMLCTL &= ~SVMLE; //关掉SVML模块
PMMCTL0_H = 0X00;
__bis_SR_register(SCG0); //该语法为固定格式,意为将括号内的变量置位,SCG0与系统工作模式有关,此时 MCLK 暂停工作
UCSCTL0 = 0; //先清零,FLL 运行时,该寄存器系统会自动配置,不用管
UCSCTL6 = (UCSCTL6&(~(XT2OFF|XT1OFF))|XCAP_3|XT2DRIVE_0);
UCSCTL3 = (5<<4)|(2<<0); // 选择 XTAL2 的时钟信号作为参考信号 并且分频到1MHz
UCSCTL4|= SELA_5;
if(Fre < 5)
UCSCTL1 = DCORSEL_2;
else if(Fre<15)
UCSCTL1 = DCORSEL_4;
else
UCSCTL1 = DCORSEL_7;
UCSCTL2 = (Fre-1);
__bic_SR_register(SCG0);
__delay_cycles(782000);
while (SFRIFG1 & OFIFG) { // Check OFIFG fault flag
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // Clear OSC flaut Flags
SFRIFG1 &= ~OFIFG; // Clear OFIFG fault flag
}
UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_3|SELM_3;
}
2.准备事项
开启LCD,关闭加热,看原理图可知,给低电平可关闭。
LCD_Init();
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);//指定区域填充颜色,清屏 防止出现重叠
GPIO_setAsOutputPin(GPIO_PORT_P1,GPIO_PIN4);
GPIO_setOutputLowOnPin(GPIO_PORT_P1,GPIO_PIN4);
3.ADC采集程序
ADC12模块中是由以下部分组成:输入的16路模拟开关,ADC内部电压参考源,ADC12内核,ADC时钟源部分,采集与保持/触发源部分,ADC数据输出部分,ADC控制寄存器等组成。
ADC 输入范围由 VREF-、VREF+ 、VDDA 、VSSA、这四个外部引脚决定。我们在设计原理图的时候一般把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到 ADC 的输入电压范围为:0~3.3V。
void setupADC(void)
{
#define ADCpin GPIO_PORT_P6,GPIO_PIN0
GPIO_setAsPeripheralModuleFunctionOutputPin(ADCpin); // 复位P6.0
ADC12_A_init(ADC12_A_BASE,ADC12_A_SAMPLEHOLDSOURCE_SC, ADC12_A_CLOCKSOURCE_ADC12OSC, ADC12_A_CLOCKDIVIDER_1); //软件触发, 一分频,内部振荡器MODCLK 5MHZ数字振荡器作为时钟源
ADC12_A_enable(ADC12_A_BASE); //启用ADC12_A模块
//设置并启用采样定时器脉冲,这里是使用的软件触发的形式,所以选择失能
ADC12_A_setupSamplingTimer(ADC12_A_BASE,ADC12_A_CYCLEHOLD_16_CYCLES,ADC12_A_CYCLEHOLD_16_CYCLES,ADC12_A_MULTIPLESAMPLESDISABLE);
ADC12_A_configureMemoryParam param = {0};
param.memoryBufferControlIndex = ADC12_A_MEMORY_0; //将内存缓冲配置为MEMORY_0
param.inputSourceSelect = ADC12_A_INPUT_A0; //将输入A0映射到内存缓冲区0,因为P6.0引脚对应A0
param.positiveRefVoltageSourceSelect = ADC12_A_VREFPOS_AVCC; //正电压为AVcc
param.negativeRefVoltageSourceSelect = ADC12_A_VREFNEG_AVSS; //负电压为AVss
param.endOfSequence = ADC12_A_NOTENDOFSEQUENCE; //单通道转换
ADC12_A_configureMemory(ADC12_A_BASE,¶m); //
}
由ADC转换的电压变化太小,不能准确体现按键变化,因而在此不做换算。
unsigned long readADC(void){
//开始从MEMORY_0中进行单通道连续转换
ADC12_A_startConversion(ADC12_A_BASE,ADC12_A_MEMORY_0,ADC12_A_SINGLECHANNEL);
while(ADC12_A_isBusy(ADC12_A_BASE) == ADC12_A_BUSY){
// 等待转换完成
}
//读取ADC转换之后寄存器的值
long result = ADC12_A_getResults(ADC12_A_BASE, ADC12_A_MEMORY_0);
return result;//直接返回
}
由于采集速率过快导致获得的数据不稳定,在此进行简单的处理,直接用延迟1毫秒取5次得平均值。
a1=readADC();
delay_ms(1);
a2=readADC();
delay_ms(1);
a3=readADC();
delay_ms(1);
a4=readADC();
delay_ms(1);
a5=readADC();
delay_ms(1);
a=(a1+a2+a3+a4+a5)/5;
4.逻辑设计
代码中的范围是通过多次试验得出,界定好范围,进行按键旋钮功能分配。
if( (a<3500)&&(a>3400) ) //换菜单,调大小
{
if( view==1 )
{
Flag_Page_1++;
if( Flag_Page_1==4 )
{
Flag_Page_1=1;
}
}
else if( view==2 )
{
switch(Flag_Page_1)
{
case 1:
X0++;
if( X0==6 ) X0=0;
break;
case 2:
Y0++;
if( Y0==11 ) Y0=0;
break;
case 3:
T0++;
if( T0==11 ) T0=1;
break;
}
}
}
else if( (a<2900)&&(a>2700) ) //进入
{
if( view==0 )
{
view=1;
}
else if( view==1 ) view=2;
Flag_Sure=1;
X0=X;Y0=Y;T0=T;
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);//指定区域填充颜色,清屏 防止出现重叠
}
else if( (a<2100)&&(a>1800) ) //退出
{
Flag_Sure=0;
if( view==2 )
{
view=1;
}
else if( view==1 ) view=0;
}
if( view==2 )
{
if( (a<3300)&&(a>3100) ) //保存
{
Flag_Save=1;
}
}
在每个界面中都逐一进行配置,在此不一一列举。
if( view==0 ) //图片
else if( view==1 ) //主页面
else if( view==2 ) //调节数值
六、过程感受
1.主程序频率的设置
刚开始设置发现程序执行的太慢,还以为是程序代码写的不好,上网查了才发现是430的本身配置频率太慢了,在网上下载、学习了各种资料才学会配置修改,进而改了主频。
2.旋钮的模拟变化量
旋钮只有在转动卡在凸起时,电压才会变化,因此转动过程中电压变化规律并不是很清晰。而且该电路并不是太稳定,仅仅是摇晃就会改变电压模拟量,感觉这部分还有待改进。然后我使用了一点点休整,使ADC采样5次取平均,减小误差。