This is the 14th day of my participation in the August More Text Challenge. For details, see:August is more challenging
When the STM8 MCU uses the ADC function to read the values of multiple channels, the continuous mode can be used, but the continuous mode can only sample the values of one channel at a time, so what if you want to sample multiple channels? The STM8 provides a multi-channel continuous sampling scan mode. In other words, the scanning mode is available only when multiple channels are sampled. The scanning mode starts from channel 0 and the second scanning mode automatically starts after the scanning is complete. Unlike the single-scan mode, the ADC automatically shuts down when the sequence is over and the second scan needs to be manually turned on. The continuous scan mode only needs to be turned on once, and the sampling will be repeated until it is manually turned off.
In continuous scan mode, there is only one more line of code in initialization than in single scan mode, which is to set the COUNT bit to 1.
Let’s start by analyzing which bits of which registers need to be set to use the continuous scan mode.
The first step is to set the maximum sampling channel. For single sampling, the channel number in the ADC_CSR register refers to the channel number to be sampled, which channel is set to, and in scan mode, the channel number refers to the maximum channel number to be scanned. All scans start from channel 0. For example, you want to scan channels 2, and 3. So the channel value here has to be set to the maximum channel value to sample, which is set to 3, so the system will scan from 0 to 3, even if 0 and 1 are not used, the system will still scan.
Next, look at the ADC_CR1 register. There are two bits to set in this register, the ADON bit and the COUNT bit. The ADON bit is used to control the ADC conversion switch. The COUNT bit is used to enable continuous conversion mode. The default value of the pre-division bit is 0, which is exactly in line with our requirements, that is, the default 2-division.
In order to use the SCAN mode, you must set the SCAN bit to 1, and use the default values for the other bits. If the data alignment is left aligned, this is not required. Right alignment is used here, so you need to set the ALIGN bit to 1 as well. The other bits default to 0.
Finally, this ADC_TDR register is used to disable Schmidt flip-flop, which is mainly used to reduce the power consumption of single chip microcomputer. Of course, this register can also be set. If you want to set, which ADC channels are used, the corresponding channel position is 1.
Now you can start writing code:
#include "adc.h"
#include "main.h"
#include "led.h"
_Bool ADC_flag = 0; //ADC conversion success flag
u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};
//AD channel pin initialization
void ADC_GPIO_Init( void )
{
PD_DDR &= ~( 1 << 2 ); // set PD2 as input
PD_CR1 &= ~( 1 << 2 ); //PD2 is set to dangling input
PD_DDR &= ~( 1 << 3 ); // set PD3 as input
PD_CR1 &= ~( 1 << 3 ); //PD3 is set to dangling input
PC_DDR &= ~( 1 << 4 ); //PC4 is set as input
PC_CR1 &= ~( 1 << 4 ); //PC4 is set to dangling input
}
// Set the scanning mode to continuous
//ch converts AIN0 continuously for the ADC channel
void ADC_CH_Init( u8 ch )
{
char l = 0;
ADC_GPIO_Init();
ADC_CR1 &= ~( 7 << 4 ); // Predivide frequency 2
ADC_CR2 &= ~( 1 << 6 ); // Do not use external triggers
// Disable the SCHMIDT trigger in AIN2 AIN4 to reduce the static power consumption of THE IO channel PD5,PD6.
ADC_TDRL |= ( 1 << 2 );
ADC_TDRL |= ( 1 << 3 );
ADC_TDRL |= ( 1 << 4 );
ADC_CR1 |= ( 1 << 1 ); // Continuous conversion
ADC_CSR |= 0x04; // Configure the channel with the largest channel number
ADC_CR2 |= ( 1 << 3 ); / / right alignment
ADC_CR1 |= ( 1 << 0 ); / / open the ADC
ADC_CR2 |= ( 1 << 1 ); // SCAN = 1 Enable the SCAN mode
// When the ADON bit is set for the first time, the ADC wakes up from low power mode. The ADON bit of the ADC_CR1 register must be set a second time using the write instruction to start the conversion.
for( l = 0; l < 10; l++ ); // Delay to ensure that the ADC module is powered on for at least 7us
ADC_CR1 |= ( 1 << 0 ); // Enable ADC in the lowest position 1 of register CR1 again and start the conversion
}
/* Note: in scan mode (continuous scan mode), do not use the bit operation instruction (BRES) to clear the EOC flag bit. This is because it is a read-modifie-write operation on the entire ADC_CSR register. Reading the current channel number from the CH[3:0] register and writing back to it will change the final channel number of the scan series. The correct way to clear the EOC flag bit in continuous scan mode is to load a byte in a RAM variable into the ADC_CSR register, which clears the EOC flag bit and also reloads the new last channel number of the scan series. It is found that the bit operation instruction only clears the value in the CH[3:0] register in continuous scan mode, but does not affect other values. Therefore, read the value from ADC_CSR, add the original channel number from CH[3:0], and write it back to ADC_CSR. Writing is as follows: ADC1 - > CSR = (uint8_t) (ADC1 - > CSR & (~ ADC1_FLAG_EOC) | ADC1_CHANNEL_n); Note: ADC1_CHANNEL_n indicates the end of the channel scanned. * /
u16 ain2_val = 0,ain3_val = 0,ain4_val = 0;
// Read the sample voltage
u16 ReadVol_CHx( void )
{
u16 voltage = 0;
u16 temph = 0;
u8 templ = 0;
while( 1) { LED = ! LED;// It takes 10us to run a loop
while( ( ADC_CSR & 0x80) = =0 ); // Wait for the transition to finish
//ADC_CSR &= ~( 1 << 7 ); // It is not possible to clear the EOC flag by bit operation
ADC_CSR = ADC_CSR & 0x7F | 0x04; // Clear the end of conversion flag EOC
// Read the value of AIN2
templ = ADC_DB2RL;
temph = ADC_DB2RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain2_val = temph;// Read the value of AIN3
templ = ADC_DB3RL;
temph = ADC_DB3RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain3_val = temph;// Read the value of AIN4
templ = ADC_DB4RL;
temph = ADC_DB4RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain4_val = temph; }return voltage;
}
Copy the code
There are two things to note here:
First, in the scanning mode, the sampled data results are not stored in the ADC_DR register, but in the ADC_DBxR register, the channel is stored in the corresponding ADC_DBxR register, this register is divided into high and low two. For example, the data on channel 2 is stored in the ADC_DB2RL and ADC_DB2RH registers. When reading the data, note that if the data is left aligned, the higher 8 bits must be read before the lower bits. If the data is right-aligned, read the lower 8 bits first and read the higher 8 bits first.
Data alignment is detailed in the official manual and should be used with caution.
Second, after the data is read, the EOC flag bit should not be cleared in the way of bit clearing. For example, in a single sampling, the flag bit is usually cleared in the way of ADC_CSR &= 0x7F. Directly clear the highest bit to 0. In continuous scan mode, this will cause problems in clearing flag bits. Because the system performs a read-modify-write operation when a bit is operated on the ADC_CSR register, the channel number will be changed during the read/rewrite process. The channel changes automatically in continuous scan mode, so the bit operation will disrupt the channel number operation of the scan channel, resulting in sampling errors. So how do you clear this flag bit? The proper way to clear the EOC flag bit in continuous scan mode is to load a byte in a RAM variable into the ADC_CSR register, which clears the EOC flag bit and reloads the new last channel number of the scan series.
This may sound abstract, but to put it simply, do not read or rewrite the register, but write the ADC_CSR value to it once and for all. So here’s the way to do it
ADC_CSR = ADC_CSR & 0x7F | 0x04;
The register is reset to zero, and then the channel number is reset once. These values are written to the register once, so that ADC_CSR will reset its value at the end of the round and continue scanning from zero, without interrupting the ADC’s channel scanning process.
The official documentation for this also explains, when using this point to pay attention to.
The above code reads the data in the usual way of querying the flag bit, but it can also read the data in the interrupt way.
#include "adc.h"
#include "main.h"
#include "led.h"
_Bool ADC_flag = 0; //ADC conversion success flag
u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};
//AD channel pin initialization
void ADC_GPIO_Init( void )
{
PD_DDR &= ~( 1 << 2 ); // set PD2 as input
PD_CR1 &= ~( 1 << 2 ); //PD2 is set to dangling input
PD_DDR &= ~( 1 << 3 ); // set PD3 as input
PD_CR1 &= ~( 1 << 3 ); //PD3 is set to dangling input
PC_DDR &= ~( 1 << 4 ); //PC4 is set as input
PC_CR1 &= ~( 1 << 4 ); //PC4 is set to dangling input
}
// Set the scanning mode to continuous
//ch converts AIN0 continuously for the ADC channel
void ADC_CH_Init( u8 ch )
{
char l = 0;
ADC_GPIO_Init();
ADC_CR1 &= ~( 7 << 4 ); // Predivide frequency 2
ADC_CR2 &= ~( 1 << 6 ); // Do not use external triggers
// Disable the SCHMIDT trigger in AIN2 AIN4 to reduce the static power consumption of THE IO channel PD5,PD6.
ADC_TDRL |= ( 1 << 2 );
ADC_TDRL |= ( 1 << 4 );
ADC_CR1 |= ( 1 << 1 ); // Continuous conversion
ADC_CSR |= 0x04; // Configure the channel with the largest channel number
ADC_CR2 |= ( 1 << 3 ); / / right alignment
ADC_CR1 |= ( 1 << 0 ); / / open the ADC
ADC_CR2 |= ( 1 << 1 ); // SCAN = 1 Enable the SCAN mode
ADC_CSR |= ( 1 << 5 ); //EOCIE enables the transition to end the interrupt
// When the ADON bit is set for the first time, the ADC wakes up from low power mode. The ADON bit of the ADC_CR1 register must be set a second time using the write instruction to start the conversion.
for( l = 0; l < 10; l++ ); // Delay to ensure that the ADC module is powered on for at least 7us
ADC_CR1 |= ( 1 << 0 ); // Enable ADC in the lowest position 1 of register CR1 again and start the conversion
}
/* Note: in scan mode (continuous scan mode), do not use the bit operation instruction (BRES) to clear the EOC flag bit. This is because it is a read-modifie-write operation on the entire ADC_CSR register. Reading the current channel number from the CH[3:0] register and writing back to it will change the final channel number of the scan series. The correct way to clear the EOC flag bit in continuous scan mode is to load a byte in a RAM variable into the ADC_CSR register, which clears the EOC flag bit and also reloads the new last channel number of the scan series. It is found that the bit operation instruction only clears the value in the CH[3:0] register in continuous scan mode, but does not affect other values. Therefore, read the value from ADC_CSR, add the original channel number from CH[3:0], and write it back to ADC_CSR. Writing is as follows: ADC1 - > CSR = (uint8_t) (ADC1 - > CSR & (~ ADC1_FLAG_EOC) | ADC1_CHANNEL_n); Note: ADC1_CHANNEL_n indicates the end of the channel scanned. * /
u16 ain2_val = 0, ain3_val = 0, ain4_val = 0;
u16 temph = 0;
u8 templ = 0;
// Read the sample voltage
u16 ReadVol_CHx( void )
{
u16 voltage = 0;
if( ADC_flag == 1 )
{
ADC_flag = 0;
// Single-channel scan mode, conversion results stored in the ADC_DBxR register
// Read the value of AIN2
templ = ADC_DB2RL;
temph = ADC_DB2RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain2_val = temph;// Read the value of AIN3
templ = ADC_DB3RL;
temph = ADC_DB3RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain3_val = temph;// Read the value of AIN4
templ = ADC_DB4RL;
temph = ADC_DB4RH;
temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8)); ain4_val = temph; }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 &= ~( 1 << 7 ); // It is not possible to clear the EOC flag by bit operation
ADC_CSR = ADC_CSR & 0x7F | 0x04; // Clear the end of conversion flag EOC
ADC_flag = 1; // Set the ADC interrupt flag to 1
}
Copy the code
When a channel scan is completed, an interrupt will be generated, and then the channel value can be read from the corresponding ADC_DBxR register. After the reading, there is no need to manually turn on the ADON flag bit, the system will automatically start the next conversion.