实现目标
实现以蜂鸣器为播放设备,能够对简谱乐曲进行解码播放。
具有循环列表,可实时切换上下曲目,实时暂停和开始,实时通过齿轮电位器调节播放音量。
能够将歌曲列表等信息,通过串口向上位机传输并显示。
硬件资源
芯片资源使用情况
P1.3 P1.4 P1.5:使用了3个io作为按键输入
P1.7:一个ADC通道采集电位器的变化情况
P1.6:一个定时器a的PWM输出通道
P1.0:一个io输出接到led作为运行状态显示
P1.1 P1.2:串口1映射到printf()上,实现在上位机打印信息的功能
外接硬件
按键x3 (4.7k电阻x3,我的电路接的是按下为高电平,是为了失效实验板原来P1.3的按下为低电平的按键)
1k齿轮电位器x3 (510欧电阻x1,也可以选择其他的组合,原则上在降低最小电流的情况下尽量提高可测量的范围)
低电平触发的蜂鸣器模块x1 (无源蜂鸣器,淘宝两三块一个,不需要加放大电路直接可以用)
程序实现
本播放器的主体功能代码来自于RT-Thread的播放器教程,本身用于Kiel下的STM32单片机。由于原始程序需要OS提供的时间片轮转支持,对于移植时候的逻辑构建造成了很大障碍,所以在本工程之前没有将其移植到MSP上的类似案例。
RT-Thread教程请点这里
开发环境配置
一开始想使用CCS进行工程开发,可以很轻松的利用官方硬件驱动。但是由于未知的原因,CCS对存储简谱的数组疯狂报错,导致最终选择转移到IAR下完成了工程。
相较于CCS,IAR下新工程需要配置的内容更为简洁
首先在工程设置中将Device选成当前使用的芯片型号
然后将Debugger中的Driver选项从模拟改成硬件
此两步之后就完成了对于新建工程的配置,至于添加PATH的操作和其他开发工具基本一致。
注意:CCS下使用的头文件在IAR下容易报错,需要改换成"io430g2553.h"
如果需要使用中断,则还需#include “in430.h”
各部分硬件驱动
LED
#include "led.h"
#include "io430g2553.h"
#include int led_init(void) { /* 设定 LED 引脚为输出模式 */ P1DIR = LED_PIN_R; P1OUT &= ~LED_PIN_R; return 0; } int led_on(void) { /* 调用 API 输出低电平 */ P1OUT |= LED_PIN_R; return 0; } int led_off(void) { /* 调用 API 输出高电平 */ P1OUT &= ~LED_PIN_R; return 0; } int led_toggle(void) { /* 调用 API 读出当前电平 然后输出相反电平 */ P1OUT ^= LED_PIN_R; return 0; } PWM #include "io430g2553.h" #define DEADTIME 20 //预设死区时间,以TA的clk为单位 /*******设定TA输出IO口,目前设定为MSP430G2553,20Pin封装无TA0.2********/ #define TA01_SET P1SEL |= BIT6; P1DIR |= BIT6 //P1.6 #define TA02_SET P3SEL |= BIT0; P3DIR |= BIT0 //P3.0 #define TA11_SET P2SEL |= BIT2; P2DIR |= BIT2 //P2.2 #define TA12_SET P2SEL |= BIT4; P2DIR |= BIT4 //P2.4 #define TA01_OFF P1SEL&= ~BIT6 //P1.6 #define TA02_OFF P3SEL &= ~BIT0 //P3.0 #define TA11_OFF P2SEL &= ~BIT2 //P2.2 #define TA12_OFF P2SEL &= ~BIT4 //P2.4 char TA0_PWM_Init(char Clk,char Div,char Mode1,char Mode2) { TA0CTL =0; // 清除以前设置 switch(Clk) //为定时器TA选择时钟源 { case 'A': case 'a': TA0CTL|=TASSEL_1; break; //ACLK case 'S': case 's': TA0CTL|=TASSEL_2; break; //SMCLK case 'E': TA0CTL|=TASSEL_0; break; //外部输入(TACLK) case 'e': TA0CTL|=TASSEL_3; break; //外部输入(TACLK取反) default : return(0); //设置参数有误,返回0 } switch(Div) //为定时器TA选择分频系数 { case 1: TA0CTL|=ID_0; break; //1 case 2: TA0CTL|=ID_1; break; //2 case 4: TA0CTL|=ID_2; break; //4 case 8: TA0CTL|=ID_3; break; //8 default : return(0); //设置参数有误,返回0 } switch(Mode1) //为定时器选择计数模式 { case 'F': case 'f': //普通PWM TA0CTL |=MC_1; break; //主定时器为增计数 case 'B':case 'b': TA0CTL |=MC_1; break; //主定时器为增计数 case 'D': case 'd': //死区PWM TA0CTL |=MC_3; break; //主定时器为增减计数 default : return(0); //其他情况都是设置参数有误,返回0 } switch(Mode1) //设置PWM通道1的输出模式。 { case 'F': case 'f': TA0CCTL1 = OUTMOD_7; TA01_SET; break; case 'B': case 'b': TA0CCTL1 = OUTMOD_3; TA01_SET; break; case 'D': case'd': TA0CCTL1 = OUTMOD_6; TA01_SET; break; case '0':case 0: //如果设置为禁用 TA01_OFF; //TA0.1恢复为普通IO口 break; default : return(0); //设置参数有误,返回0 } switch(Mode2) //设置PWM通道2的输出模式。 { case 'F': case 'f': TA0CCTL2 = OUTMOD_7; TA02_SET; break; case 'B': case 'b': TA0CCTL2 = OUTMOD_3; TA02_SET; break; case 'D': case 'd': TA0CCTL2 = OUTMOD_2; TA02_SET; break; case '0':case 0: //如果设置为禁用 TA02_OFF; //TA0.1恢复为普通IO口 break; default : return(0); //设置参数有误,返回0 } return(1); } char TA0_PWM_SetPeriod(unsigned int Period) { if (Period>65535) return(0); TA0CCR0 = 12000/Period; return(1); } char TA0_PWM_SetPermill(char Channel,unsigned int Duty) { unsigned char Mod = 0; unsigned int DeadPermill=0; unsigned long int Percent=0; //防止乘法运算时溢出 Percent=Duty; DeadPermill=((DEADTIME*1000)/TACCR0); //将绝对死区时间换算成千分比死区时间 switch (Channel) //先判断出通道的工作模式 { case 1: Mod = (TA0CCTL1& 0x00e0)>>5; break; //读取输出模式,OUTMOD0位于5-7位 case 2: Mod = (TA0CCTL2 & 0x00e0)>>5; break; //读取输出模式,OUTMOD1位于5-7位 default: return(0); } switch(Mod) //根据模式设定TACCRx { case 2: case 6: /**死区模式2,6时,需要判断修正死区时间,且同时设定TA0CCR1/2 的值*/ { if((1000-2*Percent)<=DeadPermill) //预留死区时间 Percent=(1000-DeadPermill)/2; TA0CCR1=Percent*TA0CCR0/1000; TA0CCR2= TA0CCR0-TA0CCR1; break; } case 7: { if(Percent>1000) Percent=1000; if(Channel==1) TA0CCR1=Percent* TA0CCR0/1000; if(Channel==2) TA0CCR2=Percent* TA0CCR0/1000; break; } case 3: //占空比一律为正脉宽,所以需要 TA0CCR0减去占空比 { if(Percent>1000) Percent=1000; if(Channel==1) TA0CCR1= TA0CCR0-Percent*TA0CCR0/1000; if(Channel==2) TA0CCR2= TA0CCR0-Percent*TA0CCR0/1000; break; } default: return(0); } return (1); } TA1的驱动函数与TA0相同 TA0_PWM_SetPeriod()此函数中,TA0CCR0 = 12000/Period 的12k应该改为你所配置的低速外设时钟速度,才能获得正确的声音频率 BEEP #include "beep.h" #include #include "io430g2553.h" #include "TA_PWM.h" int beep_init(void) { /* 初始化BEEP设备 */ // BCSCTL1 = CALBC1_8MHZ; // DCOCTL = CALDCO_8MHZ; /* TA0CTL = TASSEL_1 + MC_1 + ID_0; // //TA0设为增计数模式,时钟=ACLK */ return 0; } int beep_on(void) { //使能蜂鸣器对应的 PWM 通道 TA0_PWM_Init('A',1,'F',0); return 0; } int beep_off(void) { //失能蜂鸣器对应的 PWM 通道 TA0_PWM_Init('A',1,0,0); //A 12kHz return 0; } int beep_set(uint16_t freq, uint8_t volume) { // uint32_t period, pulse; TA0_PWM_SetPeriod(freq); /* 根据声音大小计算占空比 蜂鸣器低电平触发 */ /*pulse = period - period / 100 * volume;*/ TA0_PWM_SetPermill(7,1000-10*volume); return 0; } 实现了pwm驱动之后蜂鸣器只需要这几个接口就能正常使用 KEY #include "io430g2553.h" #include "in430.h" #include void key_init(void) { P1REN |=BIT3; P1OUT &= ~BIT3; P1DIR &= ~BIT3; P1REN |=BIT4; P1OUT &= ~BIT4; P1DIR &= ~BIT4; P1REN |=BIT5; P1OUT &= ~BIT5; P1DIR &= ~BIT5; } void scan_key(void) { if(P1IN&BIT3) { __delay_cycles(10000); NEXT_FLAG = 1; while(P1IN&BIT3); } if(P1IN&BIT4) { __delay_cycles(10000); STOP_FLAG = 1; while(P1IN&BIT4); } if(P1IN&BIT5) { __delay_cycles(10000); LAST_FLAG = 1; while(P1IN&BIT5); } } 使用中断模式容易打断ADC模块的转换,所以采用了扫描模式来读取按键状态 ADC #include "io430g2553.h" #include "in430.h" #include float ADC_value=0; float valum; int volume_a; void change_volume(void) { __delay_cycles(1000); // Wait for ADC Ref to settle ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start __bis_SR_register(CPUOFF + GIE); // Low Power Mode 0 with interrupts enabled ADC_value = ADC10MEM; valum =((ADC_value-333.0)*100)/688.0; // Assigns the value held in ADC10MEM to the integer called ADC_value volume_a=100-valum; if(volume_a >= 90)volume_a = 90; else if(volume_a <= 1)volume_a = 1; } void adc_init(void) { BCSCTL2 &= ~(DIVS_3); // SMCLK = DCO = 1MHz P1SEL |= BIT7; // ADC input pin P1.7 ADC10CTL1 = INCH_7 + ADC10DIV_3 ; // Channel 3, ADC10CLK/3 ADC10CTL0 = SREF_0 + ADC10SHT_3 + ADC10ON + ADC10IE; // Vcc & Vss as reference, Sample and hold for 64 Clock cycles, ADC on, ADC interrupt enable
上一篇:MSP430学习笔记-IO端口
下一篇:基于MSP430F5529单片机实现HC-SR04超声波测距 CCS & IAR
推荐阅读最新更新时间:2024-11-14 00:05