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

[复制链接]
1997|7
 楼主| 你跺你也麻zZ 发表于 2022-3-19 10:03 | 显示全部楼层 |阅读模式
#申请原创##技术资源#   

@21小跑堂
最近决定用国民技术的单片机来替代STM32单片机,在移植过程中其他都很顺利,只有ADC采集和滴答延时函数有一些问题,问题描述如下:
ADC采样问题

先放出ADC初始化的代码


  1. static void adc_gpio_init(void)  
  2. {  
  3.         GPIO_InitType GPIO_InitStructure;
  4.        
  5.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC , ENABLE ); //使能 ADC1 通道时钟
  6.         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2 , ENABLE ); //使能 ADC1 通道时钟
  7.         ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV6);   //设置 ADC 分频因子 6,64M/6=12,ADC 最大时间不能超过 14M

  8.         //PC1,2,15 作为模拟通道输入引脚
  9.         GPIO_InitStructure.Pin = AD_INPUT2_INDEX | AD_INPUT3_INDEX | AD_INPUT4_INDEX;
  10.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
  11.         GPIO_InitPeripheral(GPIOC, &GPIO_InitStructure); //初始化 GPIOC       



  12.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA , ENABLE ); //使能 ADC1 通道时钟
  13.         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2 , ENABLE ); //使能 ADC1 通道时钟
  14.         ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV6);   //设置 ADC 分频因子 6,64M/6=12,ADC 最大时间不能超过 14M

  15.         //PC1,2,15 作为模拟通道输入引脚
  16.         GPIO_InitStructure.Pin = AD_INPUT5_INDEX;
  17.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
  18.         GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure); //初始化 GPIOA       
  19. }  

  20. static void adc_dma_init(void)  
  21. {  
  22.   DMA_InitType DMA_InitStructure;  
  23.   RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE);  
  24.   
  25.   DMA_DeInit(DMA1_CH8);  //将DMA的通道1寄存器重设为缺省值
  26.   
  27.   DMA_InitStructure.PeriphAddr  = (u32) & (ADC2->DAT);  //DMA外设ADC基地址
  28.   DMA_InitStructure.MemAddr      = (u32)& ad_value;  //DMA内存基地址
  29.   DMA_InitStructure.Direction                 = DMA_DIR_PERIPH_SRC;  //内存作为数据传输的目的地
  30.   DMA_InitStructure.Mem2Mem                 = DMA_M2M_DISABLE;   //DMA通道x没有设置为内存到内存传输
  31.   DMA_InitStructure.PeriphDataSize  = DMA_PERIPH_DATA_SIZE_HALFWORD; //数据宽度为16位
  32.   DMA_InitStructure.MemDataSize      = DMA_MemoryDataSize_HalfWord;  //数据宽度为16位
  33.   DMA_InitStructure.BufSize          = ADC_CHANNEL_NUM;  //DMA通道的DMA缓存的大小
  34.   DMA_InitStructure.DMA_MemoryInc           = DMA_MEM_INC_ENABLE;  //内存地址寄存器递增
  35.   DMA_InitStructure.PeriphInc       = DMA_PERIPH_INC_DISABLE;//外设地址寄存器不变  
  36.   DMA_InitStructure.CircularMode                = DMA_MODE_CIRCULAR;  //工作在循环缓存模式
  37.   DMA_InitStructure.Priority            = DMA_PRIORITY_HIGH;  //DMA通道 x拥有高优先级
  38.   DMA_Init(DMA1_CH8, &DMA_InitStructure);  
  39.        
  40. }

  41. static void adc_init()  
  42. {  
  43.   ADC_InitType ADC_InitStructure;  
  44.   
  45.   ADC_DeInit(ADC_CHANNEL);  //将外设 ADC1 的全部寄存器重设为缺省值
  46.        
  47.   ADC_InitStructure.WorkMode = ADC_WORKMODE_INDEPENDENT;  //ADC工作模式:ADC1和ADC2工作在独立模式
  48.   ADC_InitStructure.MultiChEn = ENABLE;  //模数转换工作在扫描模式
  49.   ADC_InitStructure.ContinueConvEn = ENABLE;  //模数转换工作在连续转换模式
  50.   ADC_InitStructure.ExtTrigSelect = ADC_EXT_TRIGCONV_NONE;  //外部触发转换关闭
  51.   ADC_InitStructure.DatAlign = ADC_DAT_ALIGN_R;  //ADC数据右对齐
  52.   ADC_InitStructure.ChsNumber = ADC_CHANNEL_NUM;  //顺序进行规则转换的ADC通道的数目
  53.   ADC_Init(ADC_CHANNEL, &ADC_InitStructure);  //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
  54.        
  55.   //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
  56.   //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
  57.   ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_01_PA4, 1, ADC_SAMP_TIME_55CYCLES5);  
  58.   ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_07_PC1, 2, ADC_SAMP_TIME_55CYCLES5);  
  59.   ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_08_PC2, 3, ADC_SAMP_TIME_55CYCLES5);
  60.   ADC_ConfigRegularChannel(ADC_CHANNEL, ADC2_Channel_12_PC5, 4, ADC_SAMP_TIME_55CYCLES5);
  61.   
  62.   ADC_EnableDMA(ADC_CHANNEL, ENABLE);  // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)  
  63.   ADC_Enable(ADC_CHANNEL, ENABLE);  //使能指定的ADC1
  64.   //ADC_StartCalibration(ADC_CHANNEL);  //复位指定的ADC1的校准寄存器
  65.   
  66.   while(ADC_GetFlagStatusNew(ADC_CHANNEL,ADC_FLAG_RDY) == RESET);   //获取ADC1复位校准寄存器的状态,设置状态则等待
  67.   ADC_StartCalibration(ADC_CHANNEL);  //开始指定ADC1的校准状态
  68.   while(ADC_GetCalibrationStatus(ADC_CHANNEL));  //获取指定ADC1的校准程序,设置状态则等待
  69. }  

  70. static void adc_start(void)  
  71. {  
  72.   ADC_EnableSoftwareStartConv(ADC_CHANNEL, ENABLE); // start convert  
  73.   DMA_EnableChannel(DMA1_CH8, ENABLE);  
  74. }  

  75. void  ADCInit(void)  
  76. {  
  77.   unsigned char i = 0;
  78.        
  79.   adc_gpio_init();  
  80.   adc_dma_init();  
  81.   adc_init();  
  82.   adc_start();  
  83. }  
打印出的AD值如下图

由上图可以看出AD值十分不稳定,最多可相差300多,12位精度AD值的话300就是7.3%,误差很大了

程序上找不到头绪,先拿示波器打了一下AD管脚的波形,如下图

加电容滤波后的波形如下图

可以看到,加电容滤波后的波形好一点,但是也是不稳定.

同时测了一下STM32的AD输入波形,如下图

可以看到STM32就算不加电容波形也是十分平滑


仔细观察受干扰波形的周期发现,10ms的周期和电路上交流电压过零检测的周期一模一样,于是测了一下交流电压过零检测的波形,发现两者同步,于是猜测是过零检测电路的问题,由于过零检测电压幅值可以达到3.8V,而这个AD采样管脚在数据手册上的耐压值并不能达到5V,于是改进了过零检测电路,增加了一个跟随器,问题解决.



滴答延时问题

话不多说直接上图

左边是N32,右边是STM32

  1. void systick_delay_init(void)
  2. {
  3.         SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);        //选择AHB8分频作为systick输入时钟,8MHz  这里不能使用AHBCLK/8
  4.         //fac_us=SystemCoreClock/8000000;                                                        //为系统时钟的1/8
  5.         fac_us = 8;
  6.         fac_ms=(u16)fac_us*1000;                                                                //非OS下,代表每个ms需要的systick时钟数                
  7. }               

  8. //延时nus,nus为要延时的us数.                                                                                      
  9. void delay_us(u32 nus)
  10. {               
  11.         u32 temp;                     
  12.         SysTick->LOAD=nus*fac_us;                                         //时间加载                           
  13.         SysTick->VAL=0x00;                                                //清空计数器
  14.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数          
  15.         do
  16.         {
  17.                 temp=SysTick->CTRL;
  18.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  19.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  20.         SysTick->VAL =0X00;                                               //清空计数器       
  21. }

  22. //延时nms
  23. //注意nms的范围
  24. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  25. //nms<=0xffffff*8*1000/SYSCLK
  26. //SYSCLK单位为Hz,nms单位为ms
  27. //对72M条件下,nms<=1864
  28. void delay_ms(u16 nms)
  29. {                                     
  30.         u32 temp;                  
  31.         SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
  32.         SysTick->VAL =0x00;                                                        //清空计数器
  33.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  
  34.         do
  35.         {
  36.                 temp=SysTick->CTRL;
  37.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  38.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  39.         SysTick->VAL =0X00;                                               //清空计数器                      
  40. }


天意无罪 发表于 2022-3-19 17:38 | 显示全部楼层
楼主,这个示波器不错啊。

评论

除了开机有点慢,其他都不错的  发表于 2022-3-21 09:19
沧桑小草 发表于 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
沧桑小草 发表于 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模块初始化失败。

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

评论

好的,谢谢  发表于 2022-3-30 10:42
asmine 发表于 2022-4-1 16:45 | 显示全部楼层
挺好!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

4

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部