Tiva单片机——麦克风声音数据的储存/回放(Flash读/写)
前言:本人为一名大学生,第一次写博客,如有错误,还请多多指正。之后也会将自己学到的东西分享到博客上,欢迎关注。
一、整体介绍
1、 第一次设计
我之前使用的是Ti公司的MSP430G2553,但由于该单片机的主时钟频率太低,外加内部Flash空间少的可怜,所以在前一次的设计中,并未使用Flash空间,直接采取了ADC采集后直接G2盖板DAC转出的办法。(由于G2设计有点拉跨,就不放代码了。如果有兴趣,可以私我,我私发)。
2、 第二次设计
第二次设计中我采用了同样是Ti公司的另一款单片机-TivaTM4123GXL,这款单片机的Flash较大,并且主频可以达到80MHz,基本可以实现录音10秒并且播放的要求。
3、 最终实现情况
目前这个设计已经基本完成,实现了按键对录音/播放的控制、手机(蓝牙)对录音/播放的控制、在不同状态下会有不同的LED灯光、录音和播放的速度在代码层面可调。大部分函数已经完成封装。单片机上电后处于待机状态。
二、代码的分段解读
1、 头函数
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_flash.h"
#include "inc/hw_ints.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "driverlib/timer.h"
#include "utils/uartstdio.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"
#include "driverlib/adc.h"
#include "inc/hw_adc.h"
#include "DAC.h" //这个是我自己写的,里面的函数后面会有提到。
#include "assert.h"
#include "driverlib/flash.h"
用到了很多头文件,有缺的文件可以去TI官网找。
2、 宏定义
//宏定义
#define STARTDRESS 0x4000 //Flash起始地址
#define ENDDRESS 0x40000 //Flash终止地址
//PF1 红色 待机
//PF2 蓝色 正在录音
//PF3 绿色 录音完成
//状态变量定义
uint32_t FoundPlayorStopOrder=0; //单片机声音处理命令 0待机状态 1录音状态 2播放状态
uint32_t Initialization=0; //单片机初始化状态 0初始化中 1初始化完成
uint32_t ButtonPlayorStop; //单片机按键中断处理状态 0按键中断处理完毕1按键中断处理中
uint32_t UartPlayorStopLanya; //单片机蓝牙串口中断处理状态 0串口中断处理完毕1串口中断处理中
uint32_t TimeAPlayorStop; //单片机timeA中断处理状态
uint32_t FlashWriteorRead; //单片机Flash处理状态 0写入或读取完成 1正在写入或读取
uint32_t ADC0WriteorRead; //单片机ADC中断处理状态 0 ADC采集完毕 1 ADC采集中 2ADC暂停状态
uint32_t ADC0DataT=0; //单片机ADC采集数据高低位处理状态 0待机状态 1高位处理状态 2低位处理状态
uint32_t DAC0DataT=0; //单片机DAC读取数据高低位处理状态 0待机状态 1高位处理状态 2低位处理状态
uint32_t ZanCun=0; //单片机当前状态暂存
//数据变量定义
uint32_t ADC0SignGet[1]={0}; //ADC采样数据存储数组
uint32_t FlashDateI1;//ADC采样数据高4位
uint32_t FlashDateI2;//ADC采样数据低4位
uint32_t FlashDateInput[1]; //采样数据预处理后入Flash数组
uint32_t FlashDateOutput[1];//采样数据出Flash数组
uint32_t FlashDeleteNum=0;//flash擦除数
uint32_t FlashInputNum=0;//入flash地址数
uint32_t FlashOutputNum=0;//出flash地址数
uint32_t FlashDressNow=0;//flash当前地址数
uint32_t *FlashDataDress;//读取数据指针
uint32_t FlashDateO1;//出flash数据高4位
uint32_t FlashDateO2;//出flash数据低4位
int32_t SoundLoudness[11]={-2000,-1600,-1200,-800,-400,0,400,800,1200,1600,2000};//声音音量
uint32_t SoundLoudnessNum=5;//声音音量位
uint32_t FlashOutputSpeed=15000;//出flash速度
uint32_t FlashInputSpeed=15000;//入flash速度
uint32_t NowTimeSpeed=10000;//当前time速度
uint8_t UartDateRead; //单片机UART串口数据读取
uint8_t UartDateReads[10]; //单片机UART串口数据读取数组
uint32_t UartDateReadNum=0; //单片机UART串口数据读取数组位数
uint8_t UartDateWrite; //单片机UART串口数据写入
uint32_t UartDateJudgeNum=0; //单片机UART串口数据判断位数
uint32_t UartStartJudgeNum=0; //单片机UART串口数据开始判断位数
uint32_t UartStopJudgeNum=0; //单片机UART串口数据停止判断位数
uint32_t UartRSJudgeNum=0; //单片机UART串口数据停止判断位数
uint32_t UartDSJudgeNum=0; //单片机UART串口数据停止判断位数
int LightNum=2; //单片机LED闪烁次数
#define delay_ms(n); SysCtlDelay(n*(SysCtlClockGet()/3000));
乍一看宏定义确实挺多的,emmm,尽力理解吧。
3、 Flash擦除处理函数
void FlashDelete(void)//Flash擦除
{
for(FlashDeleteNum=0;(FlashDeleteNum+STARTDRESS)<ENDDRESS;FlashDeleteNum++)
{
if(FlashDeleteNum%400==0)
{
assert(0 == FlashErase(STARTDRESS+FlashDeleteNum));//FlashErase()函数为400位一擦除,所以需要循环来擦除起始位置与终止位置之间的所有位置
}
}
}
这个函数是我利用官方函数FlashErase();写的改进代码,因为我需要擦除
#define STARTDRESS 0x4000 //Flash起始地址
#define ENDDRESS 0x40000 //Flash终止地址
之间的所有数据。而这个官方函数只能一次擦除400位。(擦除很有必要,因为不擦除的话,那个位置是写不进去数据的。)
还有一个函数是:
assert(0 == FlashProgram(FlashDateInput, STARTDRESS+4*FlashInputNum, sizeof(FlashDateInput)));//数据写入
这个函数后面timerA中断里会出现,是写入函数,能将FlashDateInput这个数组里的数据以STARTDRESS+4*FlashInputNum为起始位置,紧挨着写进去。
4、 按键部分
在按键部分采用了按键中断,并且按键中断优先级最高。代码如下:
外设定义部分:
//按键中断
IntPrioritySet(INT_GPIOF, 0);//按键中断中断优先级定义
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //使能PF时钟
GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN); //设置PF4为输入,上拉(没按就是高电平,按下就是低电平)
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); //方向为输入 推挽上拉
GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE); //PF4配置为下降沿中断 下降沿
GPIOIntRegister(GPIO_PORTF_BASE, io_interrupt); //给PF组注册一个中断函数
GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4); //开启PF4的中断
IntEnable(INT_GPIOF);
按键中断头函数部分:
void io_interrupt(void)//按键中断头文件
{
ButtonPlayorStop= GPIOIntStatus(GPIO_PORTF_BASE, true);//获取中断状态
if((ButtonPlayorStop&GPIO_PIN_4) == GPIO_PIN_4)
{
while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4));//等待按键松开
LightTwinkle(2,'R');
FlashDelete();//Flash擦除
FlashInputNum=0;//入flash地址数归零
FlashOutputNum=0;//出flash地址数归零
ADC0DataT=0;//ADC采集数据高低位处理状态归零
DAC0DataT=0;//DAC采集数据高低位处理状态归零
FlashDeleteNum=0;//flash擦除数归零
ADC0WriteorRead=1;//ADC中断处理变量初始化
FoundPlayorStopOrder=1; //单片机开启录音
TimeASpeed();
LightTwinkle(1,'B');
UARTprintf("录音开始执行\n");
}
GPIOIntClear(GPIO_PORTF_BASE,ButtonPlayorStop); //清除发生的中断标志
ButtonPlayorStop=0;
}
中间那些变量是初始化,就是按键中断触发后,我这里设置的是全部变量初始化至起始状态,然后重新开启录音。
5、 ADC0部分(模数转换)
外设定义部分:
//ADC0
IntPrioritySet(INT_ADC0SS3, 2);//ADC0中断优先级定义
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);//ADC0外设使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);//使能配置ADC0IO引脚PE5
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5);//配置ADC0IO引脚PE5
//序列发生器 | 采样数 | FIFO深度
// SS3 | 1 | 1
ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);
ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH8 | ADC_CTL_IE | ADC_CTL_END);// 启用ADC0序列0中断
ADCIntRegister(ADC0_BASE ,3, ADC0Sequence0Handler);// 启用ADC0中断头文件
ADCIntEnable(ADC0_BASE, 3);//ADC0初始化使能
IntEnable(INT_ADC0SS3);
ADCSequenceEnable(ADC0_BASE, 3);// 使能采样序列
ADC0中断头函数部分:
void ADC0Sequence0Handler(void)//ADC中断头文件
{
if(ADC0WriteorRead!=2)
{
ADC0WriteorRead=1;//ADC状态
ADCIntClear(ADC0_BASE, 3); // 清除ADC中断标志。
ADCSequenceDataGet(ADC0_BASE, 3, ADC0SignGet); // 读取ADC值
ADC0WriteorRead=0;
//_nop();
}
}
我这里采用的是ADC0 3序列采样,一次采一个(也可以使用其他序列采很多个)
6、 UART部分(蓝牙模块)
1、UART
外设定义部分:
//蓝牙UART
IntPrioritySet(INT_UART1, 1);//Uart1中断优先级定义
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);//UART外设使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);//UART IO引脚外设使能
GPIOPinConfigure(GPIO_PB0_U1RX);//配置复用功能 //读取
GPIOPinConfigure(GPIO_PB1_U1TX); //发送
GPIOPinTypeUART(GPIO_PORTB_BASE,GPIO_PIN_0|GPIO_PIN_1);//分配UART信号
//配置UART参数(这样配置可以用UARTprintf)
UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC); //使用16MHz内部高精度振荡器(PIOSC)作为UART模块时钟
UARTStdioConfig(1,9600, 16000000); //UART编号、波特率、UART时钟频率(频率要和上一行设的一致)
UARTprintf("\n请输入操作:\n(start-重新开始录音,stop-停止录音)\n");
//FIFO配置
//UARTFIFOLevelSet(UART1_BASE,UART_FIFO_TX1_8,UART_FIFO_RX1_8); //FIFO填入半满(1/8 2byte)时触发中断
//UARTFIFOEnable(UART1_BASE);
IntEnable(INT_UART1); //enable the UART interrupt
UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT); //only enable RX and TX interrupts
UARTIntRegister(UART1_BASE,UARTIntHandler);//注册中断服务函数
UART中断头函数部分:
void UARTIntHandler(void) //Uart中断头文件
{
UartPlayorStopLanya=1;
uint32_t ui32Status;
ui32Status = UARTIntStatus(UART1_BASE, true); //get interrupt status
UARTIntClear(UART1_BASE, ui32Status); //clear the asserted interrupts
while(UARTCharsAvail(UART1_BASE)) //loop while there are chars
{
UartDateRead=UARTCharGet(UART1_BASE);
UartDateReads[UartDateReadNum]=UartDateRead;
UartDateReadNum++;
}
stringcheck();
UartPlayorStopLanya=0;
}
外设没啥好说的(里面的FIFO还没太搞明白原理,所以没用,用的是普通的UART接收和发送中断。FIFO以后再学学)
蓝牙数据读取判断函数部分:
uint8_t start[10]={"start"};
uint8_t over[10]={"stop"};
uint8_t RaiseSound[10]={"rs"};
uint8_t DecreaseSound[10]={"ds"};
void stringcheck(void)
{
int StartTiger=0; //指令判断位
int StopTiger=0;
int RSTiger=0;
int DSTiger=0;
UARTprintf("请稍等···\n");
for(UartDateJudgeNum;UartDateJudgeNum<UartDateReadNum;UartDateJudgeNum++)
{
if((start[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum])||(start[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum]+32)) UartStartJudgeNum++;
if((over[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum])||(over[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum]+32)) UartStopJudgeNum++;
if((RaiseSound[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum])||(RaiseSound[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum]+32)) UartRSJudgeNum++;
if((DecreaseSound[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum])||(DecreaseSound[UartDateJudgeNum]==UartDateReads[UartDateJudgeNum]+32)) UartDSJudgeNum++;
if(UartStartJudgeNum==5)//开始
{
StartTiger=1;
UartStartJudgeNum=0;
UartStopJudgeNum=0;
UartRSJudgeNum=0;
UartDSJudgeNum=0;
LightTwinkle(2,'R');
FlashDelete();//Flash擦除
FlashInputNum=0;//入flash地址数归零
FlashOutputNum=0;//出flash地址数归零
ADC0DataT=0;//ADC采集数据高低位处理状态归零
DAC0DataT=0;//DAC采集数据高低位处理状态归零
FlashDeleteNum=0;//flash擦除数归零
ADC0WriteorRead=1;//ADC中断处理变量初始化
FoundPlayorStopOrder=1; //单片机开启录音
TimeASpeed();
LightTwinkle(1,'B');
UARTprintf("录音开始执行\n");
}
if(UartStopJudgeNum==4)//暂停
{
LightTwinkle(2,'R');
StopTiger=1;
UartStartJudgeNum=0;
UartStopJudgeNum=0;
UartRSJudgeNum=0;
UartDSJudgeNum=0;
if(FoundPlayorStopOrder!=0)
{
ZanCun=FoundPlayorStopOrder;
FoundPlayorStopOrder=0;
UARTprintf("单片机工作已经暂停\n");
}
else
{
FoundPlayorStopOrder=ZanCun;
UARTprintf("单片机已经恢复工作状态\n");
}
//FoundPlayorStopOrder=0; //单片机待机
LightTwinkle(1,'R');
}
if(UartRSJudgeNum==2)//提升音量
{
LightTwinkle(2,'R');
RSTiger=1;
UartStartJudgeNum=0;
UartStopJudgeNum=0;
UartRSJudgeNum=0;
UartDSJudgeNum=0;
if(SoundLoudnessNum<10)
{
SoundLoudnessNum++;
UARTprintf("音量已提升至%d级\n",SoundLoudnessNum);
}
else
{
UARTprintf("音量已提升至最大\n");
}
}
if(UartDSJudgeNum==2)//降低音量
{
LightTwinkle(2,'R');
DSTiger=1;
UartStartJudgeNum=0;
UartStopJudgeNum=0;
UartRSJudgeNum=0;
UartDSJudgeNum=0;
if(SoundLoudnessNum>0)
{
SoundLoudnessNum--;
UARTprintf("音量已降低至%d级\n",SoundLoudnessNum);
}
else
{
UARTprintf("音量已降低至最小\n");
}
}
}
if(StartTiger!=1&&StopTiger!=1&&RSTiger!=1&&DSTiger!=1)UARTprintf("指令无法识别,请再次输入\n");
StartTiger=0;
StopTiger=0;
RSTiger=0;
DSTiger=0;
UartStartJudgeNum=0;
UartStopJudgeNum=0;
UartRSJudgeNum=0;
UartDSJudgeNum=0;
UartDateJudgeNum=0;
UartDateReadNum=0;
}
uint8_t start[10]={“start”};
uint8_t over[10]={“stop”};
uint8_t RaiseSound[10]={“rs”};
uint8_t DecreaseSound[10]={“ds”};
在这里我设置了上面这四个个口令,非设定口令会返回 “指令无法识别,请再次输入\n”
你也可以自己再加点,具体仿照我这个就行。
2、IO口上升沿中断(蓝牙连接状态)
外设定义部分:
//上升沿捕获中断
IntPrioritySet(INT_GPIOC, 0);//按键中断中断优先级定义
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); //使能PC时钟
GPIODirModeSet(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN); //设置PC4为输入,上拉(没按就是高电平,按下就是低电平)
GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPD); //方向为输入 推挽上拉
GPIOIntTypeSet(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_RISING_EDGE); //PC4配置为下降沿中断 下降沿
GPIOIntRegister(GPIO_PORTC_BASE, io_interruptc4); //给PC组注册一个中断函数
GPIOIntEnable(GPIO_PORTC_BASE, GPIO_INT_PIN_4); //开启PC4的中断
IntEnable(INT_GPIOC);
中断头函数部分
void io_interruptc4(void)//上升沿捕获中断头函数
{
delay_ms(20);
int D;
D= GPIOIntStatus(GPIO_PORTC_BASE, true);//获取中断状态
if((D&GPIO_PIN_4) == GPIO_PIN_4)
{
UARTprintf("\n");
UARTprintf("\n");
UARTprintf("蓝牙连接成功!\n");
UARTprintf("命令表:\n 1、start-重新开始录音\n 2、stop-暂停/开始\n 3、 rs -提升音量\n 4、 ds -降低音量\n");
UARTprintf("当前音量%d\n",SoundLoudnessNum);
UARTprintf("(命令字符不区分大小写,不能有空格)\n");
if(Initialization==1)
{
UARTprintf("单片机初始化完成!\n");
}
else
{
UARTprintf("单片机初始化中···\n");
}
UARTprintf("\n");
UARTprintf("\n");
}
GPIOIntClear(GPIO_PORTC_BASE,D); //清除发生的中断标志
}
这部分函数可以实现蓝牙连接成功时,串口返回 蓝牙连接成功!
7、 TIMERA部分(定时器)
外设定义部分:
//TIMERA
IntPrioritySet(INT_TIMER0A, 3);//TIMERA中断优先级定义
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);//使能配置TIMERA定时器
TimerConfigure(TIMER0_BASE,TIMER_CFG_PERIODIC);//配置定时器模块Timer0为Periodic周期性计数模式。
TimeASpeed();//定时器速度选择
TimerIntRegister(TIMER0_BASE,TIMER_A,IntHandle_TIMER0A);//注册中断服务函数
IntEnable(INT_TIMER0A);//使能定时器模块Timer0的定时器A的中断。
TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);//TimerA重装载时触发中断
TimerEnable(TIMER0_BASE,TIMER_A);//使能定时器TimerA。
TIMERA定时时间函数部分:
void TimeASpeed(void)//timeA定时器速度定义
{
//根据当前模式选定timeA中断周期
if(FoundPlayorStopOrder==1)NowTimeSpeed=FlashInputSpeed;//录音模式
if(FoundPlayorStopOrder==2)NowTimeSpeed=FlashOutputSpeed;//播放模式
if(FoundPlayorStopOrder==0)NowTimeSpeed=10000;//待机模式
TimerLoadSet(TIMER0_BASE,TIMER_A,(SysCtlClockGet()/NowTimeSpeed)-1);
}
这个函数可以调整TIMER定时器的速度,从而达到调整播放速度的目的(甚至录音长度都能调,但会损失精度)
TIMERA中断头函数部分:
void IntHandle_TIMER0A(void)//TIMERA定时器头文件定义
{
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
if(FoundPlayorStopOrder==1)//处于录音状态时
{
LightTwinkle(1,'B');
if(ADC0WriteorRead!=2)
{
ADCIntClear(ADC0_BASE, 3); //清除中断状态标志 触发ADC转换
ADCProcessorTrigger(ADC0_BASE, 3);//为采样序列产生一个处理器触发。
}
if(ADC0WriteorRead==0)
{
ADC0DataT++;
if(ADC0DataT%2==1)
{
FlashDateI1=ADC0SignGet[0]*10000;//计算高四位
}
if(ADC0DataT%2==0)
{
FlashDateI2=ADC0SignGet[0];//读取ADC返回数据
FlashDateInput[0]=FlashDateI1+FlashDateI2;//高位与地位数据合并
FlashDressNow=STARTDRESS+4*FlashInputNum; //计算当前Flash地址
if(FlashDressNow>ENDDRESS-4)
{
ADC0WriteorRead=2; //让ADC处于终止状态
FlashInputNum=0;
FoundPlayorStopOrder=2;
UARTprintf("录音数据储存完毕\n");
LightTwinkle(1,'G');
UARTprintf("录音播放中···\n");
}
if(ADC0WriteorRead!=2)
{
FlashWriteorRead=1;
assert(0 == FlashProgram(FlashDateInput, STARTDRESS+4*FlashInputNum, sizeof(FlashDateInput)));//数据写入
FlashWriteorRead=0;
}
FlashInputNum++;
ADC0DataT=0; //ADC数据采集标准归零
}
}
}
if(FoundPlayorStopOrder==2)//处于播放状态时
{
LightTwinkle(1,'G');
DAC0DataT++;
if(DAC0DataT%2==1)
{
FlashDataDress=STARTDRESS+4*FlashOutputNum;//Flash地址位置获取
FlashWriteorRead=1;
FlashDateOutput[0]=*FlashDataDress;//Flash地址位置数据获取
FlashWriteorRead=0;
FlashDateO1=FlashDateOutput[0]/10000;//数据高四位获取
write2DAC8411(FlashDateO1+SoundLoudness[SoundLoudnessNum]);//数据高四位发送
//write2DAC8411(0);
FlashOutputNum++;
}
if(DAC0DataT%2==0)
{
FlashDateO2=FlashDateOutput[0]-FlashDateO1*10000;//数据低四位获取
write2DAC8411(FlashDateO2+SoundLoudness[SoundLoudnessNum]);//数据低四位发送
//write2DAC8411(0);
DAC0DataT=0;
}
FlashDressNow=STARTDRESS+4*FlashOutputNum; //计算当前Flash地址
if(FlashDressNow>=ENDDRESS)
{
FlashOutputNum=0; //重复播放储存的数据
DAC0DataT=0;
}
}
}
这一段代码可以说是最复杂的一段了,有各种状态的判定。
我大概解释下,因为Flash空间有限的缘故,我采用了一位32字节的位置(为啥是32字节?因为这个写入函数就是一次32字节,我也不太会改他的函数)存储两个数据的方法,就是简单的提高4位办法*10000,然后将数据两个一次发送到Flash空间。
以此类推,播放时就是反过来读取,然后拆除2个数据,再发送出来。(write2DAC8411()这个发送函数后面会有讲)
8、 LED部分(状态灯)
外设定义部分:
//LED外设配置
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);//LED IO引脚外设使能
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
LightTwinkle(1,'R');//红灯亮 单片机处于待机状态
灯光函数部分:
void LightTwinkle(uint32_t Way,uint8_t Color)
{
int Num=LightNum;
if(Way==1)
{
if(Color=='R')
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,GPIO_PIN_1);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0);
}
if(Color=='B')
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,GPIO_PIN_2);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0);
}
if(Color=='G')
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,GPIO_PIN_3);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0);
}
}
if(Way==2)
{
if(Color=='R')
{
for(Num;0<Num;Num--)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0);
delay_ms(100);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,GPIO_PIN_1);
delay_ms(100);
}
}
if(Color=='B')
{
for(Num;0<Num;Num--)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0);
delay_ms(100);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,GPIO_PIN_2);
delay_ms(100);
}
}
if(Color=='G')
{
for(Num;0<Num;Num--)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_2,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0);
delay_ms(100);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,GPIO_PIN_3);
delay_ms(100);
}
}
}
}
这段代码没啥水平,就是简单的两种模式,用啥写啥就行。
9、 数据发送部分(G2的盖板)
头文件以及宏定义部分:
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
//--------IO宏定义----------
#define SYNC_HIGH GPIOPinWrite(GPIO_PORTB_BASE,GPIO_PIN_5,GPIO_PIN_5) //b5
#define SYNC_LOW GPIOPinWrite(GPIO_PORTB_BASE,GPIO_PIN_5,0)
#define SCLK_HIGH GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_7,GPIO_PIN_7) //A7
#define SCLK_LOW GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_7,0)
#define DIN_HIGH GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_4,GPIO_PIN_4) //E4
#define DIN_LOW GPIOPinWrite(GPIO_PORTE_BASE,GPIO_PIN_4,0)
#define BITF (0x8000)
外设定义部分:
void DAC8411_Init()
{
//使能GPIOB外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
//-----设置IO为输出-----
GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7);
GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_5);
GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_4);
//-----设置IO初始状态为高-----
SCLK_HIGH;
SYNC_HIGH;
}
发送函数定义部分:
void write2DAC8411(unsigned int Data)
{
unsigned int Temp=0;
unsigned char i=0;
Temp=Data;
SYNC_LOW; //使能开始
//-----发送00,代表是非节能模式(节能就停止工作了)-----
SCLK_HIGH;
DIN_LOW; //数据0
SCLK_LOW;
SCLK_HIGH;
DIN_LOW; //数据0
SCLK_LOW;
//-----依次发送16位数据-----
for(i=0;i<16;i++) //使用DAC7311和DAC8311时i<14即可
{
SCLK_HIGH;
//-----通过位与,判断最高位是1还是0,已决定发什么数据-----
if(Temp&BITF) DIN_HIGH;
else DIN_LOW;
SCLK_LOW;
Temp=Temp<<1; //左移一位,永远发最高位
}
SYNC_HIGH; //使能禁止,数据锁存入DAC8411
}
因为采用的是MSP430G2的盖板,所以改一下DAC例程就行,把IO口寄存器的操作改成Tiva库函数的操作就行。
10、主函数
void main(void)
{
DAC8411_Init();//DAC初始化
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_20MHZ);//定义时钟
SysCtlPeripheralEnableInt();//外设定义及初始化
IntMasterEnable();//总中断开启
Initialization=1;
UARTprintf("单片机初始化完成!\n");
while(1);
}
简简单单的主函数,大家应该看得懂。
11、代码分享
三、常见问题
1、代码不好使也没报错,就是提示.out文件生成不了。
应该是链接库没整好,建议链接库去官方找,或者自己写一个头文件。
2、G2盖板有它自带的小蜂鸣器,但是代码跑起来没声音。
确实没声音,但是看示波器是有值的。
如果你非要让他发出声音,这个问题可以这样解决,在你写完你要发送的函数后加一个write2DAC8411(0);具体原理我也不清楚。但是有这个发送0的函数的话,示波器的波形就不对了。
3、封装函数太长,看不懂。
emmmm,我建议自己一段一段写,一段一段测试,搞明白再拼装,这样会好学很多。(个人的一点建议,加注释!加注释!加注释!,重要的事情说三遍)
4、蓝牙模块用哪个比较好,有没有推荐的。
【优信电子】蓝牙3.0模块 SPP透传 兼容HC-05/06从机 JDY-31
这款感觉不错,推荐使用。
————做一只萌萌的耗子萌翻世界