一、开发环境
单片机:MSP432P401R 核心开发板
Keil版本:5.24.2.0
二、思路
硬件处理 ——> ADC采集 ——> FFT 运算 ——> 数据处理
(以下是一点碎碎念————
本人这次电赛从零开始学的msp432,之前写32好歹算熟悉K5,所以没有用CCS。
上网找了一圈21年A题的 FFT 的代码,要么调不通,要么收费,最后在学长的配置基础上自己手搓了一个,所以如果你调不通那就先怀疑这代码。
总的来说,本题的软件部分确实不太难,唯一的难点大概就是FFT(?)主要还是以前没有调过msp432,用别人的 FFT 代码调了一天没调明白,到头来还是要相信自己的双手。(点头)
三、FFT 调试
1. 片上ADC:参考官方例程中的 adc14_single_conversion_repeat_timera_source,多开了一个定时器。但由于板子太老了,官网已经没有它的资料了,可以找购买渠道的商家要。
bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "driverlib.h"
#define FFT_LENGTH 1024
extern volatile uint16_t ADC_INT_Value[FFT_LENGTH];
extern uint8_t ADC_Finish_Flag;
void ADC_Init(void);
#endif /*__BSP_ADC_H*/
bsp_adc.c
#include "bsp_adc.h"
/* Statics */
volatile uint16_t ADC_INT_Value[FFT_LENGTH];
volatile uint16_t ADC_CNT;
uint8_t ADC_Finish_Flag;
static DMA_ControlTable MSP_EXP432P401RLP_DMAControlTable[16];
void ADC_Init(void)
{
/* Timer_A Continuous Mode Configuration Parameter */
const Timer_A_UpModeConfig upModeConfig = {
TIMER_A_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
TIMER_A_CLOCKSOURCE_DIVIDER_48, // SMCLK/48 = 1MHz
1999,
TIMER_A_TAIE_INTERRUPT_DISABLE, // Disable Timer ISR
TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE, // Disable CCR0
TIMER_A_DO_CLEAR // Clear Counter
};
/* Timer_A Compare Configuration Parameter */
const Timer_A_CompareModeConfig compareConfig = {
TIMER_A_CAPTURECOMPARE_REGISTER_1, // Use CCR1
TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE, // Disable CCR interrupt
TIMER_A_OUTPUTMODE_SET_RESET, // Toggle output but
1999
};
/* Initializing ADC (MCLK/1/1) */
MAP_ADC14_enableModule();
MAP_ADC14_initModule(ADC_CLOCKSOURCE_MCLK, ADC_PREDIVIDER_1, ADC_DIVIDER_1, 0);
/* Configuring GPIOs (5.5 A0) */
MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5, GPIO_PIN5, GPIO_TERTIARY_MODULE_FUNCTION);
/* Configuring ADC Memory */
MAP_ADC14_configureSingleSampleMode(ADC_MEM0, true);
MAP_ADC14_configureConversionMemory(ADC_MEM0, ADC_VREFPOS_AVCC_VREFNEG_VSS, ADC_INPUT_A0, false);
/* Configuring Timer_A in continuous mode and sourced from ACLK */
MAP_Timer_A_configureUpMode(TIMER_A0_BASE, &upModeConfig);
/* Configuring Timer_A0 in CCR1 to trigger at CCR */
MAP_Timer_A_initCompare(TIMER_A0_BASE, &compareConfig);
/* Configuring the sample trigger to be sourced from Timer_A0 and setting it
* to automatic iteration after it is triggered*/
MAP_ADC14_setSampleHoldTrigger(ADC_TRIGGER_SOURCE1, false);
/* Enabling the interrupt when a conversion on channel 1 is complete and
* enabling conversions */
MAP_ADC14_enableInterrupt(ADC_INT0);
MAP_ADC14_enableConversion();
/* Enabling Interrupts */
MAP_Interrupt_enableInterrupt(INT_ADC14);
MAP_Interrupt_enableMaster();
/* Starting the Timer */
MAP_Timer_A_startCounter(TIMER_A0_BASE, TIMER_A_UP_MODE);
}
void ADC14_IRQHandler(void)
{
uint64_t status;
status = MAP_ADC14_getEnabledInterruptStatus();
MAP_ADC14_clearInterruptFlag(status);
if (status & ADC_INT0)
{
ADC_INT_Value[ADC_CNT++] = MAP_ADC14_getResult(ADC_MEM0);
if (ADC_CNT == FFT_LENGTH)
{
ADC_CNT = 0;
ADC_Finish_Flag = 1;
}
}
}
2. FFT 代码实现:和我主页写的32的FFT基本一样,ad值没有拟合。
如何调用 #include "arm_const_structs.h" 就不说了,请参考其他大佬。
其它代码增加在 while(1) 中。
#include "arm_const_structs.h"
if (ADC_Finish_Flag)
{
ADC_Finish_Flag = 0;
for (uint16_t i = 0; i < ADC_SAMPLING_NUM; i++)
{
ADC_Value[i] = ADC_INT_Value[i];
}
/* FFT并求幅值 */
for (uint16_t i = 0; i < ADC_SAMPLING_NUM; i++)
{
FFT_Input_Buf[i * 2] = ADC_Value[i]; //实部
FFT_Input_Buf[i * 2 + 1] = 0; //虚部
}
arm_cfft_f32(&arm_cfft_sR_f32_len1024, FFT_Input_Buf, 0, 1); //FFT
arm_cmplx_mag_f32(FFT_Input_Buf, FFT_Output_Buf, ADC_SAMPLING_NUM); //运算结果求模得幅值
FFT_Output_Buf[0] = 0;
for (uint16_t i = 0; i < ADC_SAMPLING_NUM; i++)
{
FFT_Output_Buf[i] /= ADC_SAMPLING_NUM / 2;
}
3. 数据处理:这部分就是根据题目要求的一些计算,写的有点乱,就不附代码了。
四、AD7606
1. 软件SPI:注意,DB7 和 DoutA 复用,DB8 和 DoutB 复用。
// 我随便选的引脚,任意GPIO口都可
#define AD7606OS0_H GPIO_setOutputHighOnPin(GPIO_PORT_P8,GPIO_PIN5)
#define AD7606OS0_L GPIO_setOutputLowOnPin(GPIO_PORT_P8,GPIO_PIN5)
#define AD7606OS1_H GPIO_setOutputHighOnPin(GPIO_PORT_P8,GPIO_PIN6)
#define AD7606OS1_L GPIO_setOutputLowOnPin(GPIO_PORT_P8,GPIO_PIN6)
#define AD7606OS2_H GPIO_setOutputHighOnPin(GPIO_PORT_P8,GPIO_PIN7)
#define AD7606OS2_L GPIO_setOutputLowOnPin(GPIO_PORT_P8,GPIO_PIN7)
#define AD7606_CONVST_A_H GPIO_setOutputHighOnPin(GPIO_PORT_P8,GPIO_PIN2)
#define AD7606_CONVST_A_L GPIO_setOutputLowOnPin(GPIO_PORT_P8,GPIO_PIN2)
#define AD7606_CONVST_B_H GPIO_setOutputHighOnPin(GPIO_PORT_P8,GPIO_PIN3)
#define AD7606_CONVST_B_L GPIO_setOutputLowOnPin(GPIO_PORT_P8,GPIO_PIN3)
#define AD7606_SCLK_H GPIO_setOutputHighOnPin(GPIO_PORT_P7,GPIO_PIN0)
#define AD7606_SCLK_L GPIO_setOutputLowOnPin(GPIO_PORT_P7,GPIO_PIN0)
#define AD7606_RESET_H GPIO_setOutputHighOnPin(GPIO_PORT_P7,GPIO_PIN1)
#define AD7606_RESET_L GPIO_setOutputLowOnPin(GPIO_PORT_P7,GPIO_PIN1)
#define AD7606_CS_H GPIO_setOutputHighOnPin(GPIO_PORT_P7,GPIO_PIN2)
#define AD7606_CS_L GPIO_setOutputLowOnPin(GPIO_PORT_P7,GPIO_PIN2)
#define AD7606_BUSY GPIO_getInputPinValue(GPIO_PORT_P7,GPIO_PIN4)
#define AD7606_DOUTA GPIO_getInputPinValue(GPIO_PORT_P7,GPIO_PIN5)
#define AD7606_DOUTB GPIO_getInputPinValue(GPIO_PORT_P7,GPIO_PIN6)
void AD7606_STARTCONV(void)
{
AD7606_CONVST_A_L;
AD7606_CONVST_B_L;
Delay(0xF);
AD7606_CONVST_A_H;
AD7606_CONVST_B_H;
}
uint16_t ad7606_ReadBytes(void)
{
uint16_t usData = 0;
for (uint8_t i = 0; i < 16; i++)
{
AD7606_SCLK_L;
usData = usData << 1;
if(AD7606_DOUTA)
{
usData |= 0x0001;
}
AD7606_SCLK_H;
}
return usData;
}
void AD7606_RESET(void)
{
AD7606_RESET_H;
Delay(0xFF);
AD7606_RESET_L;
}
void AD7606_SETOS(uint8_t osv)
{
switch(osv)
{
case 0://000
AD7606OS0_L;
AD7606OS1_L;
AD7606OS2_L;
break;
case 1://001
AD7606OS0_H;
AD7606OS1_L;
AD7606OS2_L;
break;
case 2://010
AD7606OS0_L;
AD7606OS1_H;
AD7606OS2_L;
break;
case 3://011
AD7606OS0_H;
AD7606OS1_H;
AD7606OS2_L;
break;
case 4://100
AD7606OS0_L;
AD7606OS1_L;
AD7606OS2_H;
break;
case 5://101
AD7606OS0_H;
AD7606OS1_L;
AD7606OS2_H;
break;
case 6://110
AD7606OS0_L;
AD7606OS1_H;
AD7606OS2_H;
break;
}
}
void Delay(volatile uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
2. while(1) 增加代码:
if(!AD7606_BUSY)
{
AD7606_CS_L;
for(uint8_t i = 0; i < 8; i++)
{
datatemp[i] = ad7606_ReadBytes(); // 对应AD7606的8个通道
}
}
五、串口屏
1. 串口通信:参考官方例程中的 uart_pc_echo_12mhz_brclk,printf 重定义,再按照陶晶驰的通信协议发送就好了。(我们的设计中没有涉及到串口接收,所以没试过能不能work
#include "usart.h"
#include "usart_calc.h"
#include <string.h>
#include "bsp_led.h"
/*
EUSCI是增强型串行通信接口,USCI是普通串行通信接口
EUSCI分为A、B
A:支持串口和spi
B:支持i2c和spi
*/
uint8_t USARTA0_RX_CNT = 0;
uint8_t USARTA0_RX_BUF[USARTA0_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
typedef struct __FILE FILE;
//与PC/串口屏通信的串口初始化
void USART_A0_Init(void)
{
eUSCI_UART_Config uartConfig;
// P1.2 RX P1.3 TX
//开启GPIO复用功能
MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1, GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
// http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
uartConfig.parity = EUSCI_A_UART_NO_PARITY; //无校验
uartConfig.msborLsbFirst = EUSCI_A_UART_LSB_FIRST; // LSB
uartConfig.numberofStopBits = EUSCI_A_UART_ONE_STOP_BIT; // 1位停止位
uartConfig.uartMode = EUSCI_A_UART_MODE;
uartConfig.selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK; //时钟源
uart_baud_calc(&uartConfig, CS_getSMCLK(), 115200);
MAP_UART_initModule(EUSCI_A0_BASE, &uartConfig); //初始化串口
/* Enable UART module */
MAP_UART_enableModule(EUSCI_A0_BASE);
/* Enabling interrupts */
MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT);
MAP_Interrupt_enableInterrupt(INT_EUSCIA0);
}
//重定义printf
int fputc(int ch, FILE *f)
{
MAP_UART_transmitData(EUSCI_A0_BASE, ch & 0xFF);
return ch;
}
//重定义scanf
int fgetc(FILE *f)
{
while (EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG !=
MAP_UART_getInterruptStatus(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG));
return UART_receiveData(EUSCI_A0_BASE);
}
//串口发送一个字符
void USARTA0_SendByte(unsigned char uch_byte)
{
MAP_UART_transmitData(EUSCI_A0_BASE, uch_byte);
}
//串口发送字符串
void USARTA0_SendString(unsigned char *puch_buf)
{
uint16_t i, len;
len = strlen((char *)puch_buf);
for (i = 0; i < len; i++)
{
USARTA0_SendByte(*(puch_buf + i));
}
}
//串口A0中断函数
void EUSCIA0_IRQHandler(void)
{
uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_BASE);
MAP_UART_clearInterruptFlag(EUSCI_A0_BASE, status); //清除中断标志
if (status && EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG) //接收中断
{
USARTA0_RX_BUF[USARTA0_RX_CNT++] = MAP_UART_receiveData(EUSCI_A0_BASE);
if (USARTA0_RX_CNT == USARTA0_REC_LEN)
{
USARTA0_RX_CNT = 0;
}
}
}