设计采用STM8S105K4芯片,AIN0和AIN1分别接直流电平,配置为多通道连续扫描模式。对采样值进行200次的平均。测试中发现AIN1通道始终为零,AIN0通道正常。对照手册仔细检查了几次代码,仍未发现问题,请高手指点!
程序代码如下:
main.c代码
- void main ( void )
- {
- uint16_t x_sen_data, y_sen_data;
- uint16_t cnt = 0;
-
- CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV4);
-
- UartInit();
- AdcInit();
-
- printf("Hello, My Board.\n");
-
- while (1)
- {
- // ADC采样结束
- if (TRUE == g_samp_update_flag)
- {
- g_samp_update_flag = FALSE;
-
- // 进行调平处理
- GetAdcValue(&x_sen_data, &y_sen_data);
- printf("%d %d\n", x_sen_data, y_sen_data);
-
- AdcInit();
- }
- }
- }
复制代码
adc.c代码
- // 采样数据长度
- #define DATA_LEN (200)
- bool g_samp_update_flag = FALSE;
- ///@ 定义文件局部变量
- static uint32_t m_x_sen_sum = 0;
- static uint32_t m_y_sen_sum = 0;
- static void delay_ms(uint32_t ms)
- {
- while (ms--);
- }
- // ADC初始化
- void AdcInit(void)
- {
- // 配置ADC相关引脚
- GPIO_Init(SCR_PORT, SCR_PIN, GPIO_MODE_OUT_PP_LOW_SLOW);
- GPIO_Init(SGY_PORT, SGY_PIN, GPIO_MODE_IN_FL_NO_IT);
- GPIO_Init(SGX_PORT, SGX_PIN, GPIO_MODE_IN_FL_NO_IT);
-
- // 复位ADC1
- ADC1_DeInit();
-
- // 配置ADC1
- ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS, ADC1_CHANNEL_1, ADC1_PRESSEL_FCPU_D4,
- ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT,
- ADC1_SCHMITTTRIG_CHANNEL1, DISABLE);
- ///@ 从ADC上电到开始ADC转换要间隔7us
- // 连续转换模式
- ADC1_ScanModeCmd(ENABLE);
-
- // 使能EOC中断
- ADC1_ITConfig(ADC1_IT_EOCIE, ENABLE);
-
- // 使能全局中断
- enableInterrupts();
-
- delay_ms(200);
-
- ///@ 是否够7us?也许是转换不稳定的主要原因
- // Start ADC Conversion
- ADC1_StartConversion();
- }
- // ADC中断处理程序
- void AdcISR(void)
- {
- static uint8_t samp_times = 0;
- uint16_t adc_data[2] = { 0, 0 };
-
- // 读取采样点
- adc_data[0] = ADC1_GetBufferValue(0x00);
- adc_data[1] = ADC1_GetBufferValue(0x01);
-
- // 采样数据处理
- m_x_sen_sum += adc_data[0];
- m_y_sen_sum += adc_data[1];
- // 清除中断标志
- ADC1_ClearITPendingBit(ADC1_IT_EOC);
-
- // 采样点计数
- samp_times++;
- if (DATA_LEN == samp_times)
- {
- samp_times = 0;
- // g_sample_flag = FALSE;
- g_samp_update_flag = TRUE;
-
- // 关闭ADC
- ADC1_Cmd(DISABLE);
- }
- }
- // 获取ADC采样值
- void GetAdcValue(uint16_t *p_data_x, uint16_t *p_data_y)
- {
- *p_data_x = m_x_sen_sum / DATA_LEN;
- *p_data_y = m_y_sen_sum / DATA_LEN;
-
- m_x_sen_sum = 0;
- m_y_sen_sum = 0;
- }
复制代码
|
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();
}
}
}
一是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
希望能对你有帮助,谢谢!
RE:STM8S ADC多通道连续扫描问题
输入这个值,是说明关闭AIN1,我理解这里不采集哪些就直接输入哪些就可以了,如下:
ADC1_SCHMITTTRIG_CHANNEL2|ADC1_SCHMITTTRIG_CHANNEL3
想请教大神两个问题:
1.这个8各通道转换后的数据是按照什么顺序存储在数据缓冲区的呢
2,我可以只扫描0通道和2通道吗?不管一通道的事......谢谢大神了
第二个问题:按照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ç¶æ
按您的提示 成功了 需要重新载入扫描系列新的最后的通道号 这步是关键
赞赞赞
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;
}
非常感谢,按照“飞龙xyj”的方法,增加了如下指令,
/* Clear the ADC1 channels */
ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
/* Select the ADC1 channel */
ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
终于解决了ADC单次扫描,输出缓存寄存器无数据的问题,非常感谢!
非常感谢,按照“飞龙xyj”的方法,增加了如下指令,
/* Clear the ADC1 channels */
ADC1->CSR &= (uint8_t)(~ADC1_CSR_CH);
/* Select the ADC1 channel */
ADC1->CSR |= (uint8_t)(ADC1_CHANNEL_7);
终于解决了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;
}
代码运行正常,结果满足预期,希望对其他小伙伴,提供帮助!