你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM8S ADC多通道连续扫描问题

[复制链接]
朗峰 提问时间:2014-2-13 23:14 /
设计采用STM8S105K4芯片,AIN0和AIN1分别接直流电平,配置为多通道连续扫描模式。对采样值进行200次的平均。测试中发现AIN1通道始终为零,AIN0通道正常。对照手册仔细检查了几次代码,仍未发现问题,请高手指点!
程序代码如下:

main.c代码
  1. void main ( void )
  2. {
  3.         uint16_t x_sen_data, y_sen_data;
  4.         uint16_t cnt = 0;
  5.         
  6.         CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV4);
  7.         
  8.         UartInit();        
  9.         AdcInit();
  10.         
  11.         printf("Hello, My Board.\n");
  12.         
  13.     while (1)
  14.         {
  15.                 // ADC采样结束
  16.                 if (TRUE == g_samp_update_flag)
  17.                 {
  18.                         g_samp_update_flag = FALSE;
  19.                         
  20.                         // 进行调平处理
  21.                         GetAdcValue(&x_sen_data, &y_sen_data);
  22.                         printf("%d    %d\n", x_sen_data, y_sen_data);
  23.                         
  24.                         AdcInit();
  25.                 }
  26.         }
  27. }
复制代码

adc.c代码
  1. // 采样数据长度
  2. #define DATA_LEN      (200)

  3. bool g_samp_update_flag = FALSE;

  4. ///@ 定义文件局部变量
  5. static uint32_t m_x_sen_sum = 0;
  6. static uint32_t m_y_sen_sum = 0;

  7. static void delay_ms(uint32_t ms)
  8. {
  9.         while (ms--);
  10. }

  11. // ADC初始化
  12. void AdcInit(void)
  13. {
  14.         // 配置ADC相关引脚
  15.         GPIO_Init(SCR_PORT, SCR_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);
  16.         GPIO_Init(SGY_PORT, SGY_PIN, GPIO_MODE_IN_FL_NO_IT);
  17.         GPIO_Init(SGX_PORT, SGX_PIN, GPIO_MODE_IN_FL_NO_IT);
  18.         
  19.         // 复位ADC1
  20.         ADC1_DeInit();
  21.         
  22.         // 配置ADC1
  23.         ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_1, ADC1_PRESSEL_FCPU_D4,
  24.                   ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT,
  25.                           ADC1_SCHMITTTRIG_CHANNEL1, DISABLE);

  26.     ///@ 从ADC上电到开始ADC转换要间隔7us
  27.         // 连续转换模式
  28.         ADC1_ScanModeCmd(ENABLE);
  29.         
  30.         // 使能EOC中断
  31.         ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);
  32.         
  33.         // 使能全局中断
  34.         enableInterrupts();
  35.         
  36.         delay_ms(200);
  37.         
  38.         ///@ 是否够7us?也许是转换不稳定的主要原因
  39.         // Start ADC Conversion
  40.         ADC1_StartConversion();
  41. }

  42. // ADC中断处理程序
  43. void AdcISR(void)
  44. {
  45.         static uint8_t samp_times = 0;
  46.     uint16_t adc_data[2] = { 0, 0 };
  47.         
  48.         // 读取采样点
  49.         adc_data[0] = ADC1_GetBufferValue(0x00);  
  50.         adc_data[1] = ADC1_GetBufferValue(0x01);
  51.         
  52.         // 采样数据处理
  53.         m_x_sen_sum += adc_data[0];
  54.         m_y_sen_sum += adc_data[1];

  55.         // 清除中断标志
  56.         ADC1_ClearITPendingBit(ADC1_IT_EOC);
  57.         
  58.         // 采样点计数
  59.         samp_times++;
  60.         if (DATA_LEN == samp_times)
  61.         {
  62.                 samp_times = 0;
  63. //                g_sample_flag = FALSE;
  64.                 g_samp_update_flag = TRUE;
  65.                
  66.                 // 关闭ADC
  67.                 ADC1_Cmd(DISABLE);
  68.         }
  69. }

  70. // 获取ADC采样值
  71. void GetAdcValue(uint16_t *p_data_x, uint16_t *p_data_y)
  72. {
  73.         *p_data_x = m_x_sen_sum / DATA_LEN;
  74.         *p_data_y = m_y_sen_sum / DATA_LEN;
  75.         
  76.         m_x_sen_sum = 0;
  77.         m_y_sen_sum = 0;
  78. }
复制代码

收藏 2 评论16 发布时间:2014-2-13 23:14

举报

16个回答
飞龙xyj 回答时间:2015-11-9 15:46:15
第一个问题,如下设置后:
ADC1_ScanModeCmd(ENABLE);
ADC1_DataBufferCmd(ENABLE);
ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);
enableInterrupts();当所有通道转换结束后才会进入中断,这个时候你在中断中去读取每个通道的AD值
INTERRUPT_HANDLER(ADC1_IRQHandler, 22)
{
    /* In order to detect unexpected events during development,
       it is recommended to set a breakpoint on the following instruction.
    */
       
        //static uint8_t samp_times = 0;
            //uint16_t adc_data[8] = {0,0,0,0,0,0,0,0};

        ADC1_IRQ_Count++;
        //samp_times++;
        // Get the ADC value of every channel
        adc_data[0] = ADC1_GetBufferValue(0x00);  
        adc_data[1] = ADC1_GetBufferValue(0x01);
          adc_data[2] = ADC1_GetBufferValue(0x02);  
        adc_data[3] = ADC1_GetBufferValue(0x03);
          adc_data[4] = ADC1_GetBufferValue(0x04);  
        adc_data[5] = ADC1_GetBufferValue(0x05);
          adc_data[6] = ADC1_GetBufferValue(0x06);  
        adc_data[7] = ADC1_GetBufferValue(0x07);

        //sum of ADC value of every channel
        channel0_adc_value_sum += adc_data[0];
        channel1_adc_value_sum += adc_data[1];
          channel2_adc_value_sum += adc_data[2];
        channel3_adc_value_sum += adc_data[3];
         channel4_adc_value_sum += adc_data[4];
        channel5_adc_value_sum += adc_data[5];
          channel6_adc_value_sum += adc_data[6];
        channel7_adc_value_sum += adc_data[7];



        // Count of the sample times
        samp_times++;

         // Sample times up to ADC_SAMPLE_TIMES
        if (ADC_SAMPLE_TIMES == samp_times)
        //if (10 == samp_times)
        {
                samp_times = 0;//Reset the samp_times
                g_samp_update_flag = TRUE;
                ADC1_Cmd(DISABLE);// Disable the ADC1
        }

        // Clear the interupt flag
        ADC1_ClearITPendingBit(ADC1_IT_EOC);

        #if 1
        /* Clear the ADC1 channels */
         ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
        /* Select the ADC1 channel */
        ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
        #endif
       
}


我是在主函数中调用如下函数去对转换得到的数据求平均
void GetAdcValue(u16 *channel0_adc1_value, u16 *channel1_adc1_value,u16 *channel2_adc1_value,u16 *channel3_adc1_value,u16 *channel4_adc1_value,u16 *channel5_adc1_value,u16 *channel6_adc1_value,u16 *channel7_adc1_value)
{
        if(g_samp_update_flag==TRUE)//End of all the ADC
        {
                g_samp_update_flag=FALSE;
               
                *channel0_adc1_value = channel0_adc_value_sum / ADC_SAMPLE_TIMES;
                *channel1_adc1_value = channel1_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel2_adc1_value = channel2_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel3_adc1_value = channel3_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel4_adc1_value = channel4_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel5_adc1_value = channel5_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel6_adc1_value = channel6_adc_value_sum / ADC_SAMPLE_TIMES;
                  *channel7_adc1_value = channel7_adc_value_sum / ADC_SAMPLE_TIMES;
                
                channel0_adc_value_sum=0;
                  channel1_adc_value_sum=0;
                  channel2_adc_value_sum=0;
                  channel3_adc_value_sum=0;
                  channel4_adc_value_sum=0;
                  channel5_adc_value_sum=0;
                  channel6_adc_value_sum=0;
                  channel7_adc_value_sum=0;

                 ADC1_Config();          
                 
        }
}

当然,上面的参数用一个数组比如ADC_Val[8],ADC_Val_Sum[8]更好,不用写这么多次

ADC配置的函数如下:
void ADC1_Config(void)
{

  ADC1_DeInit();

/*-------------------CONTINUOUS mode----------------------*/
  #if 1
  ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_7, ADC1_PRESSEL_FCPU_D8,
                  ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_ALL, DISABLE);
  #endif

  ADC1_ScanModeCmd(ENABLE);

  ADC1_DataBufferCmd(ENABLE);

  ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);

  enableInterrupts();

  DelayXms(1);
  //DelayXus(20);

  ADC1_StartConversion();

}


延时函数:
void DelayXus(u16 cnt)
{
        while(cnt--)
        {
                nop();
                 nop();
                nop();
                nop();
        }
}



void DelayXms(u16 cnt)
{
        u16 i;

          while(cnt--)
          {
                for(i=0;i<1000;i++)
                {
                        nop();
                         nop();
                        nop();
                        nop();
                }

          }
}




飞龙xyj 回答时间:2015-6-30 19:01:09
楼主您好,我今天用连续扫描模式调通了8个模拟通道(AIN0-AIN7)的AD采集,看了下您的代码,发现有两处问题:
一是ADC初始化时,设置成模拟输入的通道应该禁止施密特触发,我是全部禁止了,如下:
#if 1
  ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_7, ADC1_PRESSEL_FCPU_D8,
                  ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_ALL, DISABLE);
  #endif
你可以调用库函数ADC1_SchmittTriggerConfig(ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState NewState)去禁止你用于模拟输入的两个通道;

二是在中断后没有重新载入扫描系列新的最后的通道号,比如我的如下:
// Clear the interupt flag
        ADC1_ClearITPendingBit(ADC1_IT_EOC);

        #if 1
        /* Clear the ADC1 channels */
         ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
        /* Select the ADC1 channel */
        ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
        #endif
希望能对你有帮助,谢谢!
废鱼 回答时间:2014-2-14 10:17:48

RE:STM8S ADC多通道连续扫描问题

ADC1_SCHMITTTRIG_CHANNEL1  = (uint8_t)0x01, /**&lt; Schmitt trigger disable on AIN1 */
输入这个值,是说明关闭AIN1,我理解这里不采集哪些就直接输入哪些就可以了,如下:
ADC1_SCHMITTTRIG_CHANNEL2|ADC1_SCHMITTTRIG_CHANNEL3
你好我好大家好! 回答时间:2015-9-6 12:37:25
飞龙xyj 发表于 2015-6-30 19:01
楼主您好,我今天用连续扫描模式调通了8个模拟通道(AIN0-AIN7)的AD采集,看了下您的代码,发现有两处问题 ...

想请教大神两个问题:
1.这个8各通道转换后的数据是按照什么顺序存储在数据缓冲区的呢
2,我可以只扫描0通道和2通道吗?不管一通道的事......谢谢大神了
飞龙xyj 回答时间:2015-11-9 16:01:50
本帖最后由 飞龙xyj 于 2015-11-9 16:04 编辑
你好我好大家好! 发表于 2015-9-6 12:37
想请教大神两个问题:
1.这个8各通道转换后的数据是按照什么顺序存储在数据缓冲区的呢
2,我可以只扫描0 ...

第二个问题:按照STM8S官方参考手册,如果是连续扫描,应该是0-2之间所有通道都扫描,不管你需不需要AIN1的数据,AIN1所在的IO口都不可以设置为输出状态,因为这个IO口的输出功能被禁止了,所以扫描模式的话还是建议用连续的模拟通道,中间不要断开,不然的话就不要用扫描吧,直接单次转换,这是我个人的理解,希望能帮到你,你自己也可以多测试下!

[img]file:///C:\Users\Administrator\AppData\Roaming\Tencent\Users\545483362\QQ\WinTemp\RichOle\[Q9YMLL[0QMIEDEZR%)DU3S.png[/img]


ADC扫描模式时IO状态

ADC扫描模式时IO状态
TamTan 回答时间:2016-4-14 16:00:07
学习了,不知有没有人用过模拟看门狗
zcl201207 回答时间:2016-4-14 20:43:46
lanmanck 回答时间:2016-5-3 19:40:32
手册提到的buffer是8个还是10个,这个到底怎么定的?草
向北 回答时间:2016-12-3 15:07:35
飞龙xyj 发表于 2015-6-30 19:01
楼主您好,我今天用连续扫描模式调通了8个模拟通道(AIN0-AIN7)的AD采集,看了下您的代码,发现有两处问题 ...

按您的提示 成功了 需要重新载入扫描系列新的最后的通道号 这步是关键
赞赞赞
小小超 回答时间:2016-12-5 10:37:39
好像也太复杂了吧。
yuwentry 回答时间:2017-5-10 10:28:47
非常谢谢@飞龙xyj 的回复,也解决了我的多通道ADC采集值不对的问题,在切换通道前加一句清除通道语句即可.同时贴出我的代码备他人参考。
void ADC_Init(void)          //初始化ADC,即初始化ADC
{
       
        CLK_PCKENR2 |= SETBIT3;     //开ADC时钟
        ADC_CR2 = 0x08;             // A/D结果数据右对齐
  ADC_CR1 = 0x00;             // ADC时钟=主时钟/2=1MHZ
                              // ADC转换模式=单次
                              // 禁止ADC转换
        ADC_TDRH = 0x00;                                     //禁止施密特触发,对应的通道置1
        //ADC_TDRL = 0x38;            //0b00111000 对应3、4、5通道禁止
        ADC_TDRL = 0x30;            //0b00111000 对应3、4、5通道禁止,打开3通道
        ADC_CR1 = 0x01;           // CR1寄存器的最低位置1,使能ADC转换
        Delay_us(8);                        // 延时一段时间,至少7uS,保证ADC模块的
       
}
u16 ADC_Get_Voltage(unsigned char channel)//获取对应通道电压量化值
{
        u16 vt;
        u16 DRL,DRH;
        ADC_CSR = ADC_CSR & 0x80;             // 清除通道
        ADC_CSR = ADC_CSR | channel;          // 选择通道
        ADC_CR1 = ADC_CR1 | 0x01; // 再次将CR1寄存器的最低位置1
                              // 使能ADC转换
        while((ADC_CSR & 0x80) == 0); // 等待ADC结束
        DRL = ADC_DRL;              // 读出ADC结果的低8位
  DRH = ADC_DRH;              // 读出ADC结果的高8位       
        ADC_CSR &= CLRBIT7;  //清转换结束标志       
        vt=(DRH<<8)|DRL;  //量化值,需要 Vt/1024*Vs(Vs=3.3V )
        vt=(int)(vt*VIN*100/1024);//内部参考输入电压,100是小数点后2位都化为整数
        //vt=(int)(vt*VIN*1000/1024);  //内部参考输入电压,1000是小数点后3位都化为整数
        return vt;
}
ST007 回答时间:2017-11-5 17:36:37
飞龙xyj 发表于 2015-6-30 19:01
楼主您好,我今天用连续扫描模式调通了8个模拟通道(AIN0-AIN7)的AD采集,看了下您的代码,发现有两处问题 ...

非常感谢,按照“飞龙xyj”的方法,增加了如下指令,      
        /* Clear the ADC1 channels */
         ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
        /* Select the ADC1 channel */
        ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
终于解决了ADC单次扫描,输出缓存寄存器无数据的问题,非常感谢!
ST007 回答时间:2017-11-5 17:37:00
飞龙xyj 发表于 2015-6-30 19:01
楼主您好,我今天用连续扫描模式调通了8个模拟通道(AIN0-AIN7)的AD采集,看了下您的代码,发现有两处问题 ...

非常感谢,按照“飞龙xyj”的方法,增加了如下指令,      
        /* Clear the ADC1 channels */
         ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
        /* Select the ADC1 channel */
        ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
终于解决了ADC单次扫描,输出缓存寄存器无数据的问题,非常感谢!
ST007 回答时间:2017-11-5 17:42:59
使用的芯片为:STM8S003F3P,ADC配置为单次扫描模式,并设置ADC转化完成中断,具体如下:
void ADC1_Config(void)
{
        ADC1_DeInit();
  /* Clear the SPSEL bits */
  ADC1->CR1 &= (uint8_t)(0x8F); // 1000 1111
  /* Set the prescaler of ADC : fADC1 = fcpu/4 =4MHz*/
  ADC1->CR1 |= (uint8_t)(ADC1_PRESSEL_FCPU_D4);
        /* Set the single conversion mode */
  ADC1->CR1 &= (uint8_t)(0xFD);// 1111 1101
  /* Set the continous conversion mode */
        //ADC1->CR1 |=(uint8_t)(0x02);
        /* Clear the align bit */
  ADC1->CR2 &= (uint8_t)(0xF7);// 1111 0111
  /* Configure the data alignment */
  ADC1->CR2 |= (uint8_t)(ADC1_ALIGN_RIGHT);
       
        /* 如果定时器触发模式被选定(定时器事件作为触发源,而不是外部引脚),
        那么推荐在ADC完成设置之后启动定时器和在关闭ADC之前先停止定时器。*/
  /* Clear the external trigger selection bits */
  ADC1->CR2 &= (uint8_t)(0xCF);// 1100 1111
        /* Select TIM1 TRGO as external trigger souce */
// ADC1->CR2 |= (uint8_t)(ADC1_EXTTRIG_TIM);
  /* Enable the selected external trigger */
        //ADC1->CR2 |= (uint8_t)(0x40);  // 0100 0000
  //ADC1->CR2 &= (uint8_t)(0xBF);// 1011 1111
  /* Enable the ADC1 scan mode */
        //ADC1->CR2 |= (uint8_t)(ADC1_CR2_SCAN);
        ADC1_ScanModeCmd(ENABLE);

  /* Enables the ADC1 data store into the Data Buffer registers
  rather than in the Data Register */
        ADC1->CR3 |= ADC1_CR3_DBUF;
        /* Clear OVR flag status */
       ADC1->CR3 &= (uint8_t)(~ADC1_CR3_OVR);
//        ADC1_ClearITPendingBit(ADC1_IT_EOC);

        /* Disable EOC/AWD interrupts of ADC1 */
        //ADC1->CSR &= (uint8_t)(0xCF);// 1100 1111
        ADC1_ITConfig(ADC1_IT_EOCIE,ENABLE);//ENABLE
        ADC1_ITConfig(ADC1_IT_AWDIE,DISABLE);
  //ADC1_ITConfig((ADC1_IT_AWDIE|ADC1_IT_EOCIE),DISABLE);
        /* Clear the ADC1 channels */
        ADC1->CSR &= (uint8_t)(0xF0);//
  /* Select a ADC1 channel to be converted */
  //        ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_0);//6//5//4//3//2
  /* Clear EOC flag status */
  ADC1->CSR &= (uint8_t)(0x7F);// 0111 1111
       
  /* Disable the Schmitt Trigger of all channels on ADC1  */
  ADC1->TDRL |= (uint8_t)0xFF;
  ADC1->TDRH |= (uint8_t)0xFF;
        /*Enables or Disables the ADC1 peripheral*/
        ADC1_Cmd(ENABLE);
        Delay_us(500);//第一次上电后,等待ADC1的稳定
        /*启动一次ADC1采样及转换*/
        ADC1_Cmd(ENABLE);
}
而且我是在TIM4设置的400us时基中断中重新开启ADC1,让ADC1再进行一次新的扫描,
然后在ADC转化完成中断里,进行清除flag标志,以及做新的设置,具体如下:
INTERRUPT_HANDLER(ADC1_IRQHandler, 22)
{
/* In order to detect unexpected events during development,
                it is recommended to set a breakpoint on the following instruction.
*/


//ADC1_ClearFlag(ADC1_FLAG_EOC);//ADC1_CR3_OVR
ADC1_ClearITPendingBit(ADC1_IT_EOC);
/* Clear OVR flag status */
ADC1->CR3 &= (uint8_t)(~ADC1_CR3_OVR);

/* Clear the ADC1 channels */
ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
/* Select the ADC1 channel */
ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_6);

//GPIO_WriteReverse(GPIOA,GPIO_PIN_2);
// return;
}

代码运行正常,结果满足预期,希望对其他小伙伴,提供帮助!

12下一页
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版