ADC อ่านค่าไม่ตรงครับ STM32F107 รบกวนผู้รู้ช่วยทีครับ

  • 15 Replies
  • 4218 Views
ขอดู Code ทั้งหมดได้มั้ยครับ ส่งมาเป็นไฟล์เลยครับ

*

Offline Jimmkung

  • *
  • 12
  • Innovate for Life
    • View Profile
    • http://www.nablatronics.com/
#include "stm32f10x_lib.h"

unsigned short ADC_ConvertedValue;
char str[14];

//------------------------------ Function RCC Configuration ------------------------------------------//
void RCC_setup()
{
   ErrorStatus HSEStartUpStatus; // Keep error status
   RCC_DeInit();      // RCC system reset(for debug purpose)
   RCC_HSEConfig(RCC_HSE_ON);  // Enable HSE
   HSEStartUpStatus = RCC_WaitForHSEStartUp();   // Wait till HSE is ready
   if(HSEStartUpStatus == SUCCESS)
   {
      RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK = SYSCLK
      RCC_PCLK2Config(RCC_HCLK_Div1); // PCLK2 = HCLK
      RCC_PCLK1Config(RCC_HCLK_Div4); // PCLK1 = HCLK/2
      //RCC_ADCCLKConfig(RCC_PCLK2_Div4);  // ADCCLK = PCLK2/4
      RCC_ADCCLKConfig(RCC_PCLK2_Div6);   
      FLASH_SetLatency(FLASH_Latency_2); // Flash 2 wait state
      FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);  // Enable Prefetch Buffer
      RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);   // PLLCLK = 8MHz * 9 = 72 MHz
   
      RCC_PLLCmd(ENABLE);  // Enable PLL 
      while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);   // Wait until PLL is ready
     
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);  // Select PLL as system clock source
      while(RCC_GetSYSCLKSource() != 0x08); // Wait till PLL is used as system clock source
   }
   
}

//------------------------------ Function GPIO Configuration -----------------------------------------//
void GPIO_setup()
{
   GPIO_InitTypeDef GPIO_InitStructure;
   // Enable GPIOC clock and Enable GPIOA and AFIO clock
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
   
   // Configure PC0 as analog input(ADC Channel10)       //ADC_CH1(ADC10)
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
   GPIO_Init(GPIOC, &GPIO_InitStructure);

   // Configure USART1 Tx (PA9) as alternate function push-pull
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
   // Configure USART1 Rx (PA10) as input floating
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//------------------------------ Function ADC Configuration ------------------------------------------//
void ADC_setup()
{
   ADC_InitTypeDef ADC_InitStructure;
   // Enable ADC1 clock
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
   // ADC1 configuration
   ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
   ADC_InitStructure.ADC_ScanConvMode = DISABLE;
   ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
   ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
   ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
   ADC_InitStructure.ADC_NbrOfChannel = 1;
   ADC_Init(ADC1, &ADC_InitStructure);

   ADC_Cmd(ADC1, ENABLE);  // Enable ADC1
 
   ADC_ResetCalibration(ADC1);   // Enable ADC1 reset calibaration register
   while(ADC_GetResetCalibrationStatus(ADC1));
 
   ADC_StartCalibration(ADC1);   // Start ADC1 calibration
   while(ADC_GetCalibrationStatus(ADC1)); // Check the end of ADC1 calibration
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);    // Start ADC1 Software Conversion 
}

//------------------------------ Function USART1 Configuration -----------------------------------------//
void USART1_setup()
{
   USART_InitTypeDef USART_InitStructure;
   // Enable USART1 clock
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
   // USART1 configured
   USART_InitStructure.USART_BaudRate = 115200;
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
   USART_InitStructure.USART_StopBits = USART_StopBits_1;
   USART_InitStructure.USART_Parity = USART_Parity_No;
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
   
   USART_Init(USART1, &USART_InitStructure); // Configure the USART1
   USART_Cmd(USART1, ENABLE);  // Enable USART1
   
}


//------------------------------ Function USART1 send 1 character ------------------------------------//
void usart1_putc(unsigned char c)
{
   while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);   // Wait until transmition ready
   USART_SendData(USART1,(int)c);   // Send character
}

//------------------------------ Function USART1 send string -----------------------------------------//
void usart1_puts(unsigned char *s)
{
   while(*s)   // Check end of string
   {
      usart1_putc(*s++);   // Send charracter 1 time
   }
}

//------------------------------ Function USART1 wait character --------------------------------------//
int usart1_getc()
{
   while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);   // Wait until receive data
   return(USART_ReceiveData(USART1));  // Return  character
}

//---------------------------------------- Main Program ----------------------------------------------//
int main()
{   
   unsigned int percentage;
   
   RCC_setup();       // RCC Configuration   
   GPIO_setup();      // GPIO Configuration
   ADC_setup();       // ADC Configuration

   USART1_setup();    // USART1 Configuration

   usart1_puts("TEST\r\n");
   usart1_puts("ADC\r\n");
 
     
  while(1)       
   { 
      ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
      ADC_SoftwareStartConvCmd(ADC1, ENABLE);
      while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
      percentage = ADC_GetConversionValue(ADC1); // Convert analog to percentage(0-100% reference from 0-4095)     
      sprintf(str,"%d   ",percentage);  // Convert percentage value to string
      usart1_puts(str);       // Send character from buffer
      usart1_puts("\r\n");    // New line
      delay_ms(100);     // Delay a few time       
   }
}

อันนี้เป็นโค้ตที่ผมแปลงมาจากของคุณครับ

ผมไม่ถนัดใช้ LIB ครับ ผมชอบยิงตรง register เลย ครับ ผมว่ามันเป็นไรที่ตอบได้ดี
อาจจะเหนื่อยในการศึกษาและไม่เหมือนใครที่ใช้กันกับ LIB แต่ผมว่ามันทีเดียวจบ
ความเห็นส่วนตัวน่ะครับ
โค๊ดตัวอย่างที่เทสครับ


#include"stm32f10x.h"

unsigned int bn;
void delay_ms(unsigned int in)
                             {
                                             unsigned int jn,kn;
                                             for(jn=0;jn<in;jn++)
                                             for(kn=0;kn<6000;kn++);                                                                                                                 
                             }
void sys_clock()
                {
                 RCC->CFGR   =  0;
                 RCC->CR     =  RCC_CR_HSION;
                 while((RCC->CR & RCC_CR_HSIRDY)==0);       
                 RCC->CR     |= RCC_CR_HSEON;
                         while((RCC->CR & RCC_CR_HSERDY)==0);
                         RCC->CFGR   =  RCC_CFGR_SW_PLL|RCC_CFGR_PPRE1_DIV2|RCC_CFGR_ADCPRE_DIV6|
                                                RCC_CFGR_PLLSRC|RCC_CFGR_PLLMULL9;
                         RCC->CR     |= RCC_CR_PLLON;
                         while((RCC->CR & RCC_CR_PLLRDY)==0);       
                                   
                        }
                       
void ADC()
           {
            ADC1->CR1      =    ADC_CR1_SCAN;
            ADC1->SMPR1    =    0;        // 1.5 cycles conversion time
             ADC1->SQR3     =    14;     //channel number 14 PC4 first conversion
           
            ADC1->CR2      =    ADC_CR2_ADON;
             ADC1->CR2      |=   ADC_CR2_EXTSEL|ADC_CR2_SWSTART|ADC_CR2_EXTTRIG;//
           
            ADC1->CR2 |=ADC_CR2_RSTCAL ;    //reset calibration
            while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0x00);
           
            ADC1->CR2      |=   ADC_CR2_CAL;
            while((ADC1->CR2 & ADC_CR2_CAL) != 0x00);/* Check the end of ADC1 calibration */
       

           
            }     
               

void usartSetup (void)
                             {
                                 RCC->APB2ENR   =   0;
                                 RCC->APB2ENR   |=  RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
                                 
                                 // Put PA9  (TX) to alternate function output push-pull at 50 MHz
                      // Put PA10 (RX) to floating input
                      GPIOA->CRL       =  0;
                                 GPIOA->CRH       =  0x000004B0;                                 
                                 USART1->BRR      =  72000000/57600;
                      USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
                         
                               }


main()
      {
         int i,m,n,o,p;   
          sys_clock();
       usartSetup();
         ADC();   
          for(;
             {
                      ADC1->CR2 |= ADC_CR2_SWSTART|ADC_CR2_ADON;/* Start ADC1 Software Conversion */   
                while((ADC1->SR & ADC_SR_EOC )==0)
                ADC1->SR = 0;
                     
                     i = ADC1->DR; // keep data
                     m = i/1000;
                     n = i%1000/100;
                     o = i%100/10;
                     p = i%10;
                     
                   USART1->DR = 48+m;
                     while ((USART1->SR & USART_SR_TXE) == 0);
                     
                     USART1->DR = 48+n;
                     while ((USART1->SR & USART_SR_TXE) == 0);
                     
                     USART1->DR = 48+o;
                     while ((USART1->SR & USART_SR_TXE) == 0);
                     
                     USART1->DR = 48+p;
                     while ((USART1->SR & USART_SR_TXE) == 0);
                     
                     
                     USART1->DR = 0x0D;   // new line
                     while ((USART1->SR & USART_SR_TXE) == 0);   
                     USART1->DR = 0x0A;   // goto left
                     while ((USART1->SR & USART_SR_TXE) == 0);     
                     delay_ms(500);
                                                                           
                                 
             }
}

ลองวัด +VREF ดูครับ อาจจะไม่ไช่ 3.3V

*

Offline Jimmkung

  • *
  • 12
  • Innovate for Life
    • View Profile
    • http://www.nablatronics.com/
@คุณ deejun ผมลองเอสโค้ตคุณไปลองแล้วมัน error อะครับ ลองเอาไปทกสอบเปนส่วนๆ โดยเอาส่วน set ADC
ๆปลองปรากฎ adc ไม่ทำงานครับ

@คุณ cry wolf ผมเชคที่ขา Vref+ เปน 3.3 v ครับ ขา Vdda ก็ 3.3 v ครับ
ส่วนขา Vref- และขา Vssa ก็เปน 0 v ครับ

*

Offline Jimmkung

  • *
  • 12
  • Innovate for Life
    • View Profile
    • http://www.nablatronics.com/
ปัญหาน่าจะมาจากที่ขา Vref+ รึป่าวครับเพราะของผมมัน 3.3v แล้วที่ถูต้องมันควรจะมีค่าประมาณเท่าไรครับเพราะบอร์ดนี้ผมออกแบบเอง ผมใช้  L ที่ 2.2 mH ไม่แน่ใจว่าจะมีผลกระทบรึป่าวครับ

ปัญหาน่าจะมาจากที่ขา Vref+ รึป่าวครับเพราะของผมมัน 3.3v แล้วที่ถูต้องมันควรจะมีค่าประมาณเท่าไรครับเพราะบอร์ดนี้ผมออกแบบเอง ผมใช้  L ที่ 2.2 mH ไม่แน่ใจว่าจะมีผลกระทบรึป่าวครับ

เอาแต่ส่วน ADC ฟังก์ชันไปอย่างลืมเปิดคล๊อกให้ ADCด้วยน่ะครับ
RCC_APB2ENR_ADC1EN;
..................................................
ถ้าจะรันทั้งหมดโค๊ด มันหา .h ไม่เจอแน่เลย
ตอนคอมไฟ เซ็คสถานที่จัดเก็บใน INC ของ STM32
ให้มันรู้จัดว่าจะไปดึงที่ไหน ก็จะผ่านแล้ว อย่างลืมแอด startup.s และ system_stm32f10x.c เข้ามาเก็บในโฟรดเดอร์ที่สร้างด้วยเพื่อความสะดวก เท่านี้ก็ไม่น่าจะผิดพลาดครับ

*

Offline Jimmkung

  • *
  • 12
  • Innovate for Life
    • View Profile
    • http://www.nablatronics.com/
ขอบคุณทุกท่านมากน่ะครับที่ช่วยเหลือผมตลอดมา ตอนนี้ผมแก้ปัญหาได้แล้วครับ
software ไม่ได้มีปัญหาครับ ของที่ทุกท่านให้ผมและของที่ผมใช้ตอนแรก ใช้ได้ดีทุกๆโค้ดครับ ผมมาลองดูที่ขา Vref+ ของผม
ปรากฎว่ามัน drop ไปอยู่ที่ 2.3 V ดังนั้น ที่ 2905 จะได้เท่ากับ
 
                                                       X Volt = (2905*2.3)/4095

ซึ่งได้เท่ากับ 1.64 V ครับ ดังนั้น จริงๆแล้วโปรแกรมมันไม่ได้อ่านผิดครับ แต่ Vref+ มันไม่ถึง 3.3 V ครับ เลยทำให้ทุกท่านต้องเดือดร้อนไปด้วย

ปล. ถึงคุณ crywolf ตอนที่คุณลองให้ผมวัดที่ขา Vref+ แล้วที่ผมตอบกลับไปว่ามัน 3.3 v ผมลืมดูว่าผมเข้าโหมด Program อยู่ครับ Vref+ เลยกลายเป็น 3.3V ทำให้ผมเข้าใจผิด
ต้องขอบคุณ คุณ crywolf และคุณ deejun มากๆด้วยครับ

สำหรับคนที่ต้องการเปิดใช้ ADC ผมแนะนำ code ในหน้านี้ครับ เป็นแนวทางที่ดีที่ทุกท่านได้แนะนำไว้ครับ(ใช้ได้ทุก code ครับ conform)