【2024年电赛B题】msp432p401r代码调试过程(FFT + 外置AD7606 + 串口屏)

一、开发环境

单片机: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;
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值