This is the 15th day of my participation in the August More Text Challenge. For details, see:August is more challenging

There are altogether 5 ADC sampling channels in STM8S003, from AIN2 to AIN6. When multi-channel sampling, the ADC conversion mode needs to be set to single conversion mode. After each switchover of sampling channels, the ADC needs to be re-initialized, and the sampling results are read in interrupt.

I/O port initialization code

//AD channel pin initialization
void ADC_GPIO_Init( void )
{
    PD_DDR &= ~( 1 << 2 );              // set PD2 to input AIN3
    PD_CR1 &= ~( 1 << 2 );              //PD2 is set to dangling input
    
    PD_DDR &= ~( 1 << 3 );              //PD3 is set to input AIN4
    PD_CR1 &= ~( 1 << 3 );              //PD3 is set to dangling input
    
    PC_DDR &= ~( 1 << 4 );              //PC4 is set to input AIN2
    PC_CR1 &= ~( 1 << 4 );              //PC4 is set to dangling input
    
    PD_DDR &= ~( 1 << 5 );              //PD5 is set to input AIN5
    PD_CR1 &= ~( 1 << 5 );              //PD5 is set to dangling input
    
    PD_DDR &= ~( 1 << 6 );              //PD6 is set to input AIN6
    PD_CR1 &= ~( 1 << 6 );              //PD6 is set to dangling input
    
}
Copy the code

Set the I/O ports of the ADC to input mode.

Next, initialize the ADC functionality

void ADC_CH_Init( u8 ch )
{
    char l = 0;
    ADC_CR1  = 0x00;                    //fADC = fMASTER/2, 8Mhz single conversion, disable conversion
    ADC_CSR  = ch + 1;                  // Control the status register to select the desired AD input channel such as: PD2(AIN3)
    ADC_CR2  = 0x00;                    // By default, the data is read high first and read low first
    ADC_TDRL = ( 1 << ( ch + 1));// Disable the corresponding channel schmidt trigger function 1 to shift the left ch+1 bit
    ADC_CR1 |= 0x01;                    // Enable ADC and start conversion
    ADC_CSR |= 0x20;                    //EOCIE Enables conversion end interrupt Enables EOC interrupt
    for( l = 0; l < 100; l++ );         // Delay to ensure that the ADC module is powered on for at least 7us
    ADC_CR1 = ADC_CR1 | 0x01;           // Enable ADC in the lowest position 1 of register CR1 again and start the conversion
}
Copy the code

Set the ADC to a single conversion mode and manually set the lowest position 1 of the CR1 register for each conversion.

Read the data after a successful conversion in an interrupt.

#pragma vector = 24                              // Add 2 to the interrupt number in the STVD
__interrupt void ADC_Handle( void )
{
    ADC_CSR &= ~0x80;                            // Clear the end of conversion flag EOC
    // By default, the high 8 bits are read before the low 8 bits are read
    DATAH = ADC_DRH;                             // Read the top 8 bits of the ADC result
    DATAL = ADC_DRL;                             // Read the lower 8 bits of the ADC result
    ADC_flag = 1;                                // Set the ADC interrupt flag to 1
}
Copy the code

In interrupt, the converted sample value is stored in the DATAH and DATAL, and the sampling end flag bit ADC_flag is set. When the sampling function determines that the flag bit is 1, it reads the ADC sample value.

Sample value read function:

// Collect the voltage of PC4, AIN2
u16 ReadVol_CH2( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 1 );
    while( ADC_flag == 0 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ;        // Obtain data from 0 to 1024
        //ADC_CR1 = ADC_CR1 | 0x01; // Start the next conversion again with the lowest position 1 of the CR1 register
    };
    return voltage;
}
Copy the code

The functions of some pins of MCU need to be opened by option word. The method of setting option word by ST Visual Programmer software is as follows:

In AFR7, set PC4 to the AIN2 feature in the option word.

The complete code for ADC is as follows:

#include "adc.h"
#include "main.h"

u16  DATAH = 0;                          // The ADC converts the value 8 bits higher
u16  DATAL = 0;                          // The ADC converts the value 8 bits lower
_Bool ADC_flag = 0;                      //ADC conversion success flag


//AD channel pin initialization
void ADC_GPIO_Init( void )
{
    PD_DDR &= ~( 1 << 2 );              // set PD2 to input AIN3
    PD_CR1 &= ~( 1 << 2 );              //PD2 is set to dangling input
    
    PD_DDR &= ~( 1 << 3 );              //PD3 is set to input AIN4
    PD_CR1 &= ~( 1 << 3 );              //PD3 is set to dangling input
    
    PC_DDR &= ~( 1 << 4 );              //PC4 is set to input AIN2
    PC_CR1 &= ~( 1 << 4 );              //PC4 is set to dangling input
    
    PD_DDR &= ~( 1 << 5 );              //PD5 is set to input AIN5
    PD_CR1 &= ~( 1 << 5 );              //PD5 is set to dangling input
    
    PD_DDR &= ~( 1 << 6 );              //PD6 is set to input AIN6
    PD_CR1 &= ~( 1 << 6 );              //PD6 is set to dangling input
    
}

//ADC input channel initialization entry parameter indicates channel selection
void ADC_CH_Init( u8 ch )
{
    char l = 0;
    ADC_CR1  = 0x00;                    //fADC = fMASTER/2, 8Mhz single conversion, disable conversion
    ADC_CSR  = ch + 1;                  // Control the status register to select the desired AD input channel such as: PD2(AIN3)
    ADC_CR2  = 0x00;                    // By default, the data is read high first and read low first
    ADC_TDRL = ( 1 << ( ch + 1));// Disable the corresponding channel schmidt trigger function 1 to shift the left ch+1 bit
    ADC_CR1 |= 0x01;                    // Enable ADC and start conversion
    ADC_CSR |= 0x20;                    //EOCIE Enables conversion end interrupt Enables EOC interrupt
    for( l = 0; l < 100; l++ );         // Delay to ensure that the ADC module is powered on for at least 7us
    ADC_CR1 = ADC_CR1 | 0x01;           // Enable ADC in the lowest position 1 of register CR1 again and start the conversion
}


// Collect the voltage of PC4, AIN2
u16 ReadVol_CH2( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 1 );
    while( ADC_flag == 0 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ;        // Obtain data from 0 to 1024
        //ADC_CR1 = ADC_CR1 | 0x01; // Start the next conversion again with the lowest position 1 of the CR1 register
    };
    return voltage;
}

// Collect the PD2 voltage AIN3
u16 ReadVol_CH3( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 2 );
    while( ADC_flag == 0 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ;
        //ADC_CR1 = ADC_CR1 | 0x01; // If the channel does not need to be switched, it only needs to be initialized once. After each time the data is read, the next conversion needs to be manually enabled
    };
    return voltage;
}
// Collect the PD3 voltage value AIN4
u16 ReadVol_CH4( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 3 );
    while( ADC_flag == 0 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ;       // Obtain data from 0 to 1024
        //ADC_CR1 = ADC_CR1 | 0x01; // Start the next conversion again with the lowest position 1 of the CR1 register
    };
    return voltage;
}

// Collect the PD5 voltage value AIN5
u16 ReadVol_CH5( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 4 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ; // Obtain data from 0 to 1024
        //ADC_CR1 = ADC_CR1 | 0x01; // Start the next conversion again with the lowest position 1 of the CR1 register
    };
    return voltage;
}

// Collect the PD6 voltage value AIN6
u16 ReadVol_CH6( void )
{
    u16 voltage = 0;
    ADC_CH_Init( 5 );
    if( ADC_flag )
    {
        ADC_flag = 0;
        voltage = ( DATAH << 2 ) + DATAL ; // Obtain data from 0 to 1024
        //ADC_CR1 = ADC_CR1 | 0x01; // Start the next conversion again with the lowest position 1 of the CR1 register
    };
    return voltage;
}

//AD interrupt service function interrupt number 22
#pragma vector = 24                              // Add 2 to the interrupt number in the STVD
__interrupt void ADC_Handle( void )
{
    ADC_CSR &= ~0x80;                            // Clear the end of conversion flag EOC
    // By default, the high 8 bits are read before the low 8 bits are read
    DATAH = ADC_DRH;                             // Read the top 8 bits of the ADC result
    DATAL = ADC_DRL;                             // Read the lower 8 bits of the ADC result
    ADC_flag = 1;                                // Set the ADC interrupt flag to 1
}
Copy the code

The main function code is as follows:

#include "iostm8s103F3.h"
#include "main.h"
#include "led.h"
#include "adc.h"
#include "delay.h"

u16 val_ch2 = 0, val_ch3 = 0, val_ch4 = 0, val_ch5 = 0, val_ch6 = 0;

void SysClkInit( void )
{
    CLK_SWR = 0xe1;                             //HSI 16MHz CPU clock frequency of the primary clock source
    CLK_CKDIVR = 0x00;                          // The CPU clock is 0 and the system clock is 0
}

void main( void )
{
    u8 i=0;
    __asm( "sim" );                             // Disable interrupt
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    ADC_GPIO_Init();
    __asm( "rim" );                             // Enable interrupt
    while( 1 )
    {
        LED = ~LED; 
        
        // After the channel switch, the first batch of sampled data is still the data of the previous channel
        for(i=0; i<10; i++) val_ch2 = ReadVol_CH2(); delay_ms(100 );
        
         for(i=0; i<10; i++) val_ch3 = ReadVol_CH3(); delay_ms(100 );
        
         for(i=0; i<10; i++) val_ch4 = ReadVol_CH4(); delay_ms(100 );
        
         for(i=0; i<10; i++) val_ch5 = ReadVol_CH5(); delay_ms(100 );
        
        for(i=0; i<10; i++) val_ch6 = ReadVol_CH6(); delay_ms(100); }}Copy the code