STC8H development: Based on fwlib_ Introduction and demonstration of analog-to-digital conversion ADC of STC8

catalogue

Earlier, we introduced the use of fwlib in Keil5 and PlatformIO environments_ STC8. Next, mainly STC8H series, ADC (analog-to-digital conversion) is introduced in combination with the demonstration cases in the demo

ADC analog-to-digital conversion of STC8G and STC8H

The ADC part of STC8G and STC8H is basically the same in register setting, but the channel number, channel number and accuracy corresponding to different models are different

Number and accuracy of channels

The channel number and accuracy of each series corresponding to STC8G/STC8H are as follows

Product line ADC resolution Number of ADC channels
STC8H1K08 series 10 bits 9 channels
STC8H1K28 series 10 bits 12 channels
STC8H3K64S4 series 12 bits 12 channels
STC8H3K64S2 series 12 bits 12 channels
STC8H8K64U series 12 bits 15 channels
STC8H2K64T series 12 bits 15 channels
STC8H4K64TLR series 12 bits 15 channels
STC8H4K64TLCD series 12 bits 15 channels
STC8H4K64LCD series 12 bits 15 channels

Channel selection uses register ADC_ The lower 4 bits of contr correspond to each series of STC8G/STC8H. The channels corresponding to the values of this register are as follows

STC8H1K28 STC8H1K08 STC8H3K64S4
STC8H3K64S2
STC8H8K64U
STC8H2K64T
STC8H4K64TLR
STC8H4K64TLCD
STC8H4K64LCD
STC8G1K08A STC8G1K08
STC8G1K08T
STC8G2K64S4
STC8G2K64S2
0000 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P1.0/ADC0 P3.0/ADC0 P1.0/ADC0 P1.0/ADC0
0001 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P1.1/ADC1 P3.1/ADC1 P1.1/ADC1 P1.1/ADC1
0010 P1.2/ADC2 N/A P1.2/ADC2 P5.4/ADC2 P5.4/ADC2 P3.2/ADC2 P1.2/ADC2 P1.2/ADC2
0011 P1.3/ADC3 N/A N/A P1.3/ADC3 P1.3/ADC3 P3.3/ADC3 P1.3/ADC3 P1.3/ADC3
0100 P1.4/ADC4 N/A N/A P1.4/ADC4 P1.4/ADC4 P5.4/ADC4 P1.4/ADC4 P1.4/ADC4
0101 P1.5/ADC5 N/A N/A P1.5/ADC5 P1.5/ADC5 P5.5/ADC5 P1.5/ADC5 P1.5/ADC5
0110 P1.6/ADC6 N/A P1.6/ADC6 P1.6/ADC6 P6.2/ADC6 N/A P1.6/ADC6 P1.6/ADC6
0111 P1.7/ADC7 N/A P1.7/ADC7 P1.7/ADC7 P6.3/ADC7 N/A P1.7/ADC7 P1.7/ADC7
1000 P0.0/ADC8 P3.0/ADC8 P0.0/ADC8 P0.0/ADC8 P0.0/ADC8 N/A P3.0/ADC8 P0.0/ADC8
1001 P0.1/ADC9 P3.1/ADC9 P0.1/ADC9 P0.1/ADC9 P0.1/ADC9 N/A P3.1/ADC9 P0.1/ADC9
1010 P0.2/ADC10 P3.2/ADC10 P0.2/ADC10 P0.2/ADC10 P0.2/ADC10 N/A P3.2/ADC10 P0.2/ADC10
1011 P0.3/ADC11 P3.3/ADC11 P0.3/ADC11 P0.3/ADC11 P0.3/ADC11 N/A P3.3/ADC11 P0.3/ADC11
1100 N/A P3.4/ADC12 P0.4/ADC12 P0.4/ADC12 P0.4/ADC12 N/A P3.4/ADC12 P0.4/ADC12
1101 N/A P3.5/ADC13 P0.5/ADC13 P0.5/ADC13 P0.5/ADC13 N/A P3.5/ADC13 P0.5/ADC13
1110 N/A P3.6/ADC14 P0.6/ADC14 P0.6/ADC14 P0.6/ADC14 N/A P3.6/ADC14 P0.6/ADC14
1111 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref 1.19Vref

Alignment format of conversion results

In fact, the ADC sampling accuracy cannot be set. The sampling uses the maximum accuracy of the current model, and the results are stored in the [ADC_RES, ADC_RESL] registers To facilitate the use of results with different accuracy in different occasions, you can set the results to left alignment or right alignment

  • When left alignment is set, only ADC can be taken_ Res value (8 bits), ignoring the last two bits
  • When setting bit right alignment, ADC can be taken according to the actual accuracy_ Lower 4 bits (12 bit precision) or lower 2 bits (10 bit precision) of res, plus ADC_RESL gets the final result

Conversion time consumption

A complete ADC conversion time is = Tsetup + Tduty + Thold + Tconvert

  • Tsetup: converted channel switching time, which can be set to 1 or 2 ADC clock cycles
  • Tduty: the sampling time of conversion. The default is the lowest 11 ADC clocks and the highest is 32 ADC clock cycles
  • Thord: hold time for channel selection. You can select 1, 2, 3 or 4 ADC clock cycles
  • Tconvert: the conversion time is fixed, the 10bit accuracy is 10 ADC clocks, and the 12bit accuracy is 12 ADC clocks

The above time units are ADC clock cycles. The number of system clock (SYSCLK) occupied by each ADC clock cycle can be set. Using the lower three bits of ADCCFG register, it can be set from the lowest 2 system clock cycles to the highest 32 system clock cycles

For the highest frequency of conversion, a global limit is written on DS

  • The speed of 10 bit ADC shall not be higher than 500KHz
  • The speed of 12 bit ADC shall not be higher than 800KHz
  • The sampling time for conversion cannot be less than 10. It is recommended to set it to 15

Hardware connection

There are two kinds of ADC hardware connections for STC8G/STC8H: with avcc and agrnd and without avcc and agrnd

With AVcc,AGrnd

The high-end model STC8H3K64S2 series, for example, will have these two pin pins, which correspond to the voltage reference value and ground reference value of the conversion target respectively For ordinary use, these two can be directly connected to VCC and GND

   AGrnd   -> GND
   AVcc    -> VCC
   AVref   -> VCC 
   Vcc     -> VCC
   Gnd     -> GND
   ADC1    -> Sampling point

Without AVcc,AGrnd

The low-end model and STC8G series do not have these two pin s. They only need to be connected to AVref. The sampling point is connected to MCU in common. The connection is

   AVref   -> VCC 
   Vcc     -> VCC
   Gnd     -> GND
   ADC1    -> Test voltage

Demonstration use case description

The following demonstration case is based on FwLib_STC8 , the source code is located in FwLib_STC8/demo/adc Directory, you can download or view it yourself Due to version evolution, the code may be different from the code in the warehouse, and the latest version in the warehouse shall prevail

For how to run the demonstration case, please refer to the configuration instructions of Keil C51 and VSCode PlatformIO described earlier

ADC1 is used for 8-bit ADC conversion and active polling mode

In the following example, P1.1 is queried every 0.1 seconds by active query 1 port for ADC conversion, with an accuracy of 8 bits, and output the result to the serial port

main.c code

#include "fw_hal.h"

void main(void)
{
    uint8_t res;
    // Adjust the system frequency. If STC-ISP is used to set the frequency, this line needs to be commented out
    SYS_SetClock();
    // For result output
    UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    // Set ADC1(GPIO P1.1) as high resistance input
    GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
    // Use channel: ADC1
    ADC_SetChannel(0x01);
    // Set ADC clock = SYSCLK / 2 / (1+1) = SYSCLK / 4
    ADC_SetClockPrescaler(0x01);
    // To set the left alignment of the result, you only need to take the value ADC_RES
    ADC_SetResultAlignmentLeft();
    // Turn on the ADC
    ADC_SetPowerState(HAL_State_ON);

    while(1)
    {
        // Start conversion
        ADC_Start();
        // Wait for two system clocks
        NOP();
        NOP();
        // Check whether the conversion result flag bit is set
        while (!ADC_SamplingFinished());
        // Clear result flag bit
        ADC_ClearInterrupt();
        // Read results
        res = ADC_RES;

        // Output through serial port 1
        UART1_TxString("Result: ");
        UART1_TxHex(res);
        UART1_TxString("\r\n");
        // Wait for 100ms before switching again
        SYS_Delay(100);
    }
}

ADC1 is used for 10 bit / 12 bit ADC conversion in interrupt mode

In the following example, P1.0 is interrupted 1 port carries out ADC continuous conversion, the accuracy is 10 bits (or 12 bits, different MCU models have different accuracy), and the result is output to the serial port every 0.1 seconds

#include "fw_hal.h"

// 16 bit variables are used to record the conversion results
uint16_t res;

// The method of handling interrupts uses macro definitions to ensure the compatibility between Keil C51 and SDCC
INTERRUPT(ADC_Routine, EXTI_VectADC)
{
    // Clear the middle break first
    ADC_ClearInterrupt();
    // Result lower 8 bits
    res = ADC_RESL;
    // The result is 8 bits higher
    res |= (ADC_RES & 0x0F) << 8;
    // Start again to make the ADC convert continuously, 
    ADC_Start();
}

void main(void)
{
    // Set system frequency
    SYS_SetClock();
    // Result output
    UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    // Set P11 high resistance input mode
    GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
    // Use channel: ADC1
    ADC_SetChannel(0x01);
    // ADC clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
    ADC_SetClockPrescaler(0x0F);
    // Right aligned to facilitate conversion to double byte results
    ADC_SetResultAlignmentRight();
    // Enable global interrupt and ADC interrupt
    EXTI_Global_SetIntState(HAL_State_ON);
    EXTI_ADC_SetIntState(HAL_State_ON);
    // Turn on the ADC
    ADC_SetPowerState(HAL_State_ON);
    // Start ADC conversion
    ADC_Start();

    while(1)
    {
        // Conversion result output
        UART1_TxString("Result: ");
        UART1_TxHex(res >> 8);
        UART1_TxHex(res & 0xFF);
        UART1_TxString("\r\n");
        SYS_Delay(100);
    }
}

Use ADC1 and ADC2 dual channels for conversion and interrupt mode

The following is a more practical example. Multi channel ADC conversion in the form of interrupt can be used for wireless car remote control, dual channel audio sampling, etc

#include "fw_hal.h"

// The channel number used to record the current sample
uint8_t pos;
// Record the sampling results of each channel
uint16_t res[2];

// Interrupt processing method
INTERRUPT(ADC_Routine, EXTI_VectADC)
{
    ADC_ClearInterrupt();
    // Record sampling results
    res[pos] = ADC_RESL;
    res[pos] |= (ADC_RES & 0x0F) << 8;
    
    // Switch to the next channel
    pos = (pos+1) & 0x1;
    if (pos == 0)
    {
        /**
         * When the sampling frequency is high, adding these two sentences can improve the accuracy The mechanism is to switch to the open drain mode to clear the residual voltage on the sampling port
        GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_InOut_OD);
        GPIO_P1_SetMode(GPIO_Pin_1, GPIO_Mode_Input_HIP);
        */
        ADC_SetChannel(0x01);
    }
    else
    {
        /**
         * Uncomment these lines in high speed ADC
        GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_InOut_OD);
        GPIO_P1_SetMode(GPIO_Pin_2, GPIO_Mode_Input_HIP);
        */
        ADC_SetChannel(0x02);
    }
    ADC_Start();
}

// The following code is basically the same as the previous one, so I won't comment in detail
void main(void)
{
    SYS_SetClock();
    // For debug print
    UART1_Config8bitUart(UART1_BaudSource_Timer2, HAL_State_ON, 115200);
    // Channel: ADC1
    ADC_SetChannel(0x01);
    // ADC Clock = SYSCLK / 2 / (1+15) = SYSCLK / 32
    ADC_SetClockPrescaler(0x0F);
    // Right alignment, high 2-bit in ADC_RES, low 8-bit in ADC_RESL
    ADC_SetResultAlignmentRight();
    // Enable interrupts
    EXTI_Global_SetIntState(HAL_State_ON);
    EXTI_ADC_SetIntState(HAL_State_ON);
    // Turn on ADC power
    ADC_SetPowerState(HAL_State_ON);
    // Set ADC1(P1.1), ADC2(P1.2) HIP
    GPIO_P1_SetMode(GPIO_Pin_1|GPIO_Pin_2, GPIO_Mode_Input_HIP);
    // Start ADC
    ADC_Start();

    while(1)
    {
        UART1_TxString("Result: ");
        UART1_TxHex(res[0] >> 8);
        UART1_TxHex(res[0] & 0xFF);
        UART1_TxChar(' ');
        UART1_TxHex(res[1] >> 8);
        UART1_TxHex(res[1] & 0xFF);
        UART1_TxString("\r\n");
        SYS_Delay(100);
    }
}

end

The above is how STC8H uses FwLib_STC8 package library ADC conversion demonstration case description In practical use, the delay time accuracy under the active polling mode is not high,
If the accuracy of sampling interval is required, it is recommended to use the form of interrupt

Keywords: ADC

Added by Tarsonis21 on Sun, 09 Jan 2022 16:20:35 +0200