|
采用版上的V7-019_ADC定时器触发+DMA双缓冲实现例程,稍微加以改动,TIME1的触发时钟改为了1MHz,预期实现1MHz 16位的单路AD采样,AD接到PB1(CH5)上,调试了一周发现采样的AD值跳动非常大,现在不知道如何下手解决,求指点。
硬件用的是STM32H750V,100PIN的,硬件相关电路如下。目前测试接入一个电压,AD采样值跳动无规律,上百个字都有。


相关代码改动如下:
/* 选择ADC的时钟源 */
//#define ADC_CLOCK_SOURCE_AHB /* 选择AHB时钟源 */
#define ADC_CLOCK_SOURCE_PLL /* 选择PLL时钟源 */
/* 方便Cache类的API操作,做32字节对齐 */
uint16 temp[1024];
ALIGN_32BYTES(__attribute__((at(0x38000000))) uint16_t ADCxValues[1024]);
__IO uint8_t s_DmaFlag = 0; /* 1表示进DMA半传输完成中断,2表示进入DMA传输完成中断 */
DMA_HandleTypeDef DmaHandle = {0};
static void TIM1_Config(void)
{
TIM_HandleTypeDef htim ={0};
TIM_OC_InitTypeDef sConfig = {0};
__HAL_RCC_TIM1_CLK_ENABLE();
HAL_TIM_Base_DeInit(&htim);
//配置TIM1 1MHz
htim.Instance = TIM1;
htim.Init.Period = 199;
htim.Init.Prescaler = 0;
htim.Init.ClockDivision = 0;
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim);
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
//占空比50%
sConfig.Pulse = 500;
HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1);
}
void InitADC(void)
{
ADC_HandleTypeDef AdcHandle = {0};
ADC_ChannelConfTypeDef sConfig = {0};
GPIO_InitTypeDef GPIO_InitStruct;
/* ## - 1 - 配置ADC采样的时钟 ####################################### */
#if defined (ADC_CLOCK_SOURCE_PLL)
/* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInitStruct.PLL2.PLL2M = 25;
PeriphClkInitStruct.PLL2.PLL2N = 504;
PeriphClkInitStruct.PLL2.PLL2P = 7;
PeriphClkInitStruct.PLL2.PLL2Q = 7;
PeriphClkInitStruct.PLL2.PLL2R = 7;
PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;
PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
#elif defined (ADC_CLOCK_SOURCE_AHB)
/* 使用AHB时钟的话,无需配置,默认选择*/
#endif
/* ## - 2 - 配置ADC采样使用的引脚 ####################################### */
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* ## - 3 - 配置ADC采样使用的时钟 ####################################### */
__HAL_RCC_DMA1_CLK_ENABLE();
DmaHandle.Instance = DMA1_Stream1; /* 使用的DMA1 Stream1 */
DmaHandle.Init.Request = DMA_REQUEST_ADC1; /* 请求类型采用DMA_REQUEST_ADC1 */
DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* 传输方向是从外设到存储器 */
DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址自增禁止 */
DmaHandle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器地址自增使能 */
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据传输位宽选择半字,即16bit */
DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* 存储器数据传输位宽选择半字,即16bit */
DmaHandle.Init.Mode = DMA_CIRCULAR; /* 循环模式 */
DmaHandle.Init.Priority = DMA_PRIORITY_LOW; /* 优先级低 */
DmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
DmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* 禁止FIFO此位不起作用,用于设置阀值 */
DmaHandle.Init.MemBurst = DMA_MBURST_SINGLE; /* 禁止FIFO此位不起作用,用于存储器突发 */
DmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* 禁止FIFO此位不起作用,用于外设突发 */
HAL_DMA_Init(&DmaHandle);
/* 开启DMA1 Stream1的中断 */
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
/* 关联ADC句柄和DMA句柄 */
__HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);
/* ## - 4 - 配置ADC ########################################################### */
__HAL_RCC_ADC12_CLK_ENABLE();
AdcHandle.Instance = ADC1;
#if defined (ADC_CLOCK_SOURCE_PLL)
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; /* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */
#elif defined (ADC_CLOCK_SOURCE_AHB)
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; /* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
#endif
AdcHandle.Init.Resolution = ADC_RESOLUTION_16B; /* 16位分辨率 */
AdcHandle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 禁止扫描,因为仅开了一个通道 */
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; /* EOC转换结束标志 */
AdcHandle.Init.LowPowerAutoWait = DISABLE; /* 禁止低功耗自动延迟特性 */
AdcHandle.Init.ContinuousConvMode = DISABLE; /* 禁止自动转换,采用的定时器触发转换 */
AdcHandle.Init.NbrOfConversion = 1; /* 使用了1个转换通道 */
AdcHandle.Init.DiscontinuousConvMode = DISABLE; /* 禁止不连续模式 */
AdcHandle.Init.NbrOfDiscConversion = 1; /* 禁止不连续模式后,此参数忽略,此位是用来配置不连续子组中通道数 */
AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T1_CC1; /* 定时器1的CC1触发 */
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; /* 上升沿触发 */
AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; /* DMA循环模式接收ADC转换的数据 */
AdcHandle.Init.BoostMode = ENABLE; /* ADC时钟超过20MHz的话,使能boost */
AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; /* ADC转换溢出的话,覆盖ADC的数据寄存器 */
AdcHandle.Init.OversamplingMode = DISABLE; /* 禁止过采样 */
HAL_ADC_Init(&AdcHandle);
HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
/* 配置ADC通道 */
sConfig.Channel = ADC_CHANNEL_5; /* 配置使用的ADC通道 */
sConfig.Rank = ADC_REGULAR_RANK_1; /* 采样序列里的第1个 */
sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5; /* 采样周期 */
sConfig.SingleDiff = ADC_SINGLE_ENDED; /* 单端输入 */
sConfig.OffsetNumber = ADC_OFFSET_NONE; /* 无偏移 */
sConfig.Offset = 0; /* 无偏移的情况下,此参数忽略 */
HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);
/* ## - 5 - 配置ADC的定时器触发 ####################################### */
TIM1_Config();
HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)ADCxValues, 1024);
}
void DMA1_Stream1_IRQHandler(void)
{
/* 传输完成中断 */
if((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET)
{
/*
1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
2、进入传输完成中断,当前DMA正在使用缓冲区的前半部分,用户可以操作后半部分。
*/
SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[512]), 1024);
s_DmaFlag = 2;
/* 清除标志 */
DMA1->LIFCR = DMA_FLAG_TCIF1_5;
}
/* 半传输完成中断 */
if((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET)
{
/*
1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
2、进入半传输完成中断,当前DMA正在使用缓冲区的后半部分,用户可以操作前半部分。
*/
SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[0]), 1024);
s_DmaFlag = 1;
/* 清除标志 */
DMA1->LIFCR = DMA_FLAG_HTIF1_5;
}
/* 传输错误中断 */
if((DMA1->LISR & DMA_FLAG_TEIF1_5) != RESET)
{
/* 清除标志 */
DMA1->LIFCR = DMA_FLAG_TEIF1_5;
}
/* 直接模式错误中断 */
if((DMA1->LISR & DMA_FLAG_DMEIF1_5) != RESET)
{
/* 清除标志 */
DMA1->LIFCR = DMA_FLAG_DMEIF1_5;
}
}
void GetAdcValues(void)
{
uint16 i;
uint8 Byte0,Byte1,Byte2;
//当前DMA操作是后半部分缓冲,发送前半部分缓冲
if(s_DmaFlag == 1)
{
s_DmaFlag = 0;
if(StartChannelCodeEn)
{
uint8 Response[1042]= {0x80,0x00,0x70};
ControlBox01.StdCastByts = 1025;
IntToBytes(ControlBox01.StdCastByts,&Byte0,&Byte1,&Byte2);
Response[15] = Byte1;
Response[16] = Byte2;
Response[17]= StdRollCounter++;
for(i=0;i<512;i++)
{
Response[i*2+18] = (uint8)(ADCxValues>>8);
Response[i*2+19] = (uint8)ADCxValues;
}
EthRequestResp(udppcb,Response);
}
}
//当前DMA操作是后前部分缓冲,发送后半部分缓冲
else if(s_DmaFlag == 2)
{
s_DmaFlag = 0;
if(StartChannelCodeEn)
{
uint8 Response[1042]= {0x80,0x00,0x70};
ControlBox01.StdCastByts = 1025;
IntToBytes(ControlBox01.StdCastByts,&Byte0,&Byte1,&Byte2);
Response[15] = Byte1;
Response[16] = Byte2;
Response[17]= StdRollCounter++;
for(i=512;i<1024;i++)
{
Response[(i-512)*2+18] = (uint8)(ADCxValues>>8);
Response[(i-512)*2+19] = (uint8)ADCxValues;
}
EthRequestResp(udppcb,Response);
}
}
}
|
|