打印
[N32G45x]

国民技术N32G455单片机AD采样受干扰以及滴答延时函数

[复制链接]
1090|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创##技术资源#   

@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;                                               //清空计数器                      
}


使用特权

评论回复
沙发
天意无罪| | 2022-3-19 17:38 | 只看该作者
楼主,这个示波器不错啊。

使用特权

评论回复
评论
你跺你也麻zZ 2022-3-21 09:19 回复TA
除了开机有点慢,其他都不错的 
板凳
沧桑小草| | 2022-3-19 19:49 | 只看该作者
ADC还有个1M时钟需要配置一下,看下例程中的readme.txt,在初始化没有体现出来。另外滴答定时器是不支持8分频,这点配置以及后面计算延时时间的时候需要注意。

使用特权

评论回复
地板
你跺你也麻zZ|  楼主 | 2022-3-21 09:18 | 只看该作者
沧桑小草 发表于 2022-3-19 19:49
ADC还有个1M时钟需要配置一下,看下例程中的readme.txt,在初始化没有体现出来。另外滴答定时器是不支持8分 ...

1M时钟不配置的话会有什么影响吗,我看了有关ADC的例程里面都没有配置RCC_ConfigAdc1mClk

使用特权

评论回复
5
沧桑小草| | 2022-3-26 21:47 | 只看该作者
你跺你也麻zZ 发表于 2022-3-21 09:18
1M时钟不配置的话会有什么影响吗,我看了有关ADC的例程里面都没有配置RCC_ConfigAdc1mClk ...

ADC模块在上电过程(PowerUp)时,需要用ADC_1MCLK时钟来计数和信号同步以完成上电流程。所以如果没有ADC_1MCLK时钟,ADC_CTRL3.RDY ADC准备就绪标志不会置起,导致ADC模块初始化失败。

所以是建议要明确配置一下

使用特权

评论回复
评论
你跺你也麻zZ 2022-3-30 10:42 回复TA
好的,谢谢 
6
asmine| | 2022-4-1 16:45 | 只看该作者
挺好!!!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

4

帖子

0

粉丝