#申请原创##技术资源#
@21小跑堂
最近决定用国民技术的单片机来替代STM32单片机,在移植过程中其他都很顺利,只有ADC采集和滴答延时函数有一些问题,问题描述如下:
ADC采样问题先放出ADC初始化的代码
static void adc_gpio_init(void)
{
GPIO_InitType GPIO_InitStructure;
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC , ENABLE ); //使能 ADC1 通道时钟
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2 , ENABLE ); //使能 ADC1 通道时钟
ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV6); //设置 ADC 分频因子 6,64M/6=12,ADC 最大时间不能超过 14M
//PC1,2,15 作为模拟通道输入引脚
GPIO_InitStructure.Pin = AD_INPUT2_INDEX | AD_INPUT3_INDEX | AD_INPUT4_INDEX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_InitPeripheral(GPIOC, &GPIO_InitStructure); //初始化 GPIOC
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA , ENABLE ); //使能 ADC1 通道时钟
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2 , ENABLE ); //使能 ADC1 通道时钟
ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV6); //设置 ADC 分频因子 6,64M/6=12,ADC 最大时间不能超过 14M
//PC1,2,15 作为模拟通道输入引脚
GPIO_InitStructure.Pin = AD_INPUT5_INDEX;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure); //初始化 GPIOA
}
static void adc_dma_init(void)
{
DMA_InitType DMA_InitStructure;
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE);
DMA_DeInit(DMA1_CH8); //将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.PeriphAddr = (u32) & (ADC2->DAT); //DMA外设ADC基地址
DMA_InitStructure.MemAddr = (u32)& ad_value; //DMA内存基地址
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC; //内存作为数据传输的目的地
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; //DMA通道x没有设置为内存到内存传输
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD; //数据宽度为16位
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.BufSize = ADC_CHANNEL_NUM; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE; //内存地址寄存器递增
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;//外设地址寄存器不变
DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR; //工作在循环缓存模式
DMA_InitStructure.Priority = DMA_PRIORITY_HIGH; //DMA通道 x拥有高优先级
DMA_Init(DMA1_CH8, &DMA_InitStructure);
}
static void adc_init()
{
ADC_InitType ADC_InitStructure;
ADC_DeInit(ADC_CHANNEL); //将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.WorkMode = ADC_WORKMODE_INDEPENDENT; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.MultiChEn = ENABLE; //模数转换工作在扫描模式
ADC_InitStructure.ContinueConvEn = ENABLE; //模数转换工作在连续转换模式
ADC_InitStructure.ExtTrigSelect = ADC_EXT_TRIGCONV_NONE; //外部触发转换关闭
ADC_InitStructure.DatAlign = ADC_DAT_ALIGN_R; //ADC数据右对齐
ADC_InitStructure.ChsNumber = ADC_CHANNEL_NUM; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC_CHANNEL, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
//ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_01_PA4, 1, ADC_SAMP_TIME_55CYCLES5);
ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_07_PC1, 2, ADC_SAMP_TIME_55CYCLES5);
ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_08_PC2, 3, ADC_SAMP_TIME_55CYCLES5);
ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_12_PC5, 4, ADC_SAMP_TIME_55CYCLES5);
ADC_EnableDMA(ADC_CHANNEL, ENABLE); // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
ADC_Enable(ADC_CHANNEL, ENABLE); //使能指定的ADC1
//ADC_StartCalibration(ADC_CHANNEL); //复位指定的ADC1的校准寄存器
while(ADC_GetFlagStatusNew(ADC_CHANNEL,ADC_FLAG_RDY) == RESET); //获取ADC1复位校准寄存器的状态,设置状态则等待
ADC_StartCalibration(ADC_CHANNEL); //开始指定ADC1的校准状态
while(ADC_GetCalibrationStatus(ADC_CHANNEL)); //获取指定ADC1的校准程序,设置状态则等待
}
static void adc_start(void)
{
ADC_EnableSoftwareStartConv(ADC_CHANNEL, ENABLE); // start convert
DMA_EnableChannel(DMA1_CH8, ENABLE);
}
void ADCInit(void)
{
unsigned char i = 0;
adc_gpio_init();
adc_dma_init();
adc_init();
adc_start();
}
打印出的AD值如下图
由上图可以看出AD值十分不稳定,最多可相差300多,12位精度AD值的话300就是7.3%,误差很大了 程序上找不到头绪,先拿示波器打了一下AD管脚的波形,如下图 加电容滤波后的波形如下图 可以看到,加电容滤波后的波形好一点,但是也是不稳定. 同时测了一下STM32的AD输入波形,如下图 可以看到STM32就算不加电容波形也是十分平滑
仔细观察受干扰波形的周期发现,10ms的周期和电路上交流电压过零检测的周期一模一样,于是测了一下交流电压过零检测的波形,发现两者同步,于是猜测是过零检测电路的问题,由于过零检测电压幅值可以达到3.8V,而这个AD采样管脚在数据手册上的耐压值并不能达到5V,于是改进了过零检测电路,增加了一个跟随器,问题解决.
滴答延时问题话不多说直接上图 左边是N32,右边是STM32 void systick_delay_init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); //选择AHB8分频作为systick输入时钟,8MHz 这里不能使用AHBCLK/8
//fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
fac_us = 8;
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
}
//延时nus,nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
|