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
这款感觉不错,推荐使用。

————做一只萌萌的耗子萌翻世界

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值