ADS1115介绍
ADS1115是具有 PGA、振荡器、电压基准、比较器的 16 位、860SPS、4 通道 Δ-Σ ADC,数据通过一个 I2C 兼容型串行接口进行传输。有关它的详细说明可以参考官方数据手册。
驱动源码
头文件
源文件
使用指南
基本步骤
初始化软件模拟I2C或者硬件I2C外设(以笔者编写的软件模拟I2C库为例)
- I2C_Virtual_ConfigPort(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
- I2C_Virtual_SwitchBus(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
复制代码
初始化ADS1115芯片的配置。笔者在驱动库中提供了两种初始化配置函数void ADS1115_UserConfig1()与void ADS1115_UserConfig2(),可以修改这两个函数中的配置参数后直接调用。
设置或者切换ADS1115的通道(配置完ADS1115之后,可以设置一个初始通道)。
根据项目的需要,选择采集原始数据然后在其他地方换算成电压数据,或者直接采集电压数据以及滤波后的电压数据。
- int16_t rawData = 0;
- float voltage = 0;
-
- //获取ADS1115采集的原始16位数据
- ADS1115_ReadRawData(&rawDAta);
- //将ADS1115采集的原始16位数据换算为实际电压值
- voltage = ADS1115_RawDataToVoltage(rawData);
- //直接获取ADS1115采集的电压数据
- voltage = ADS1115_GetVoltage();
-
- //直接获取ADS1115采集的电压数据(经过多次采样计算平均值)
- voltage = ADS1115_GetAverageVoltage(10);
复制代码
注意事项
不使用实时操作系统
对于多通道采样,每次通道切换时,应当等待几毫秒的时间后再进行采样,否则采样的数据可能不稳定或者发生通道间干扰。
- int32_t ADC_Temp_Filter[4] = {0};
- float ADC_Temp[4] = {0};
-
- for (uint8_t chan = 0; chan < 4; chan++)
- {
- ADS1115_ScanChannel(chan);
- delay_ms(5);
- ADS1115_ReadRawData(&dataTemp);
- //Keep 3 decimal places and filter
- ADC_Temp_Filter[chan] = (int32_t)(ADS1115_GetVoltage()*1000);
- ADC_Temp[chan] = Filter_MovingAverage(ADC_Temp_Filter[chan],chan)/1000.0;
- }
复制代码
由于多通道切换的通道稳定等待时间的存在,上面的代码耗时将会很长(>20ms)。如果在中断服务函数中执行了这些代码,将会导致单片机的性能严重下降。因此,需要考虑在定时器更新中断服务函数中周期性的调用void ADS1115_RefreshAllChannel()。每次调用将刷新一个通道的数据,四次调用便可以将全部通道的数据都刷新一遍,大大提高了代码的运行效率。示例如下:
软件定时器1以40Hz的频率周期性的调用void ADS1115_RefreshAllChannel(),以获得10Hz的全通道数据刷新速度。
软件定时器0以10Hz的频率处理ADS1115的原始采样数据(换算成电压、滤波、校正等)与其他传感器的数据。
通过多个更新周期不同的定时器的配合,可以在无代码延时的情况下解决不同传感器数据采集速度不一致的问题,周期性的调用代替了延时等待。
- /**
- * @brief 设置软件定时器,主要包括初始化与开启各个软件定时器
- */
- void User_SetupTimer()
- {
- //Poll frequency of software timer is 1KHz
- //SW_Timer0 for refresh sensor data and modbus action. Freq. = 10Hz
- SoftwareTimer_Init(0, 100, User_SoftwareTimer0_Handler, -1);
- //SW_Timer1 for ads1115 data acquisition. Freq. = 40Hz
- SoftwareTimer_Init(1, 25, User_SoftwareTimer1_Handler, -1);
- //SW_Timer2 for RS485 scan. Freq. = 10Hz
- SoftwareTimer_Init(2, 100, User_SoftwareTimer2_Handler, -1);
- //SW_Timer3 for MTSICS. Freq. = 10Hz
- SoftwareTimer_Init(3, 100, User_SoftwareTimer3_Handler, -1);
- //SW_Timer4 for RS100 (Printer has not been used). Freq. = 10Hz
- SoftwareTimer_Init(4, 100, User_SoftwareTimer4_Handler, -1);
- SoftwareTimer_Enable(0);
- SoftwareTimer_Enable(1);
- SoftwareTimer_Enable(2);
- SoftwareTimer_Enable(3);
- SoftwareTimer_Enable(4);
- }
- /**
- * @brief 软件定时器1的更新服务函数
- */
- void User_SoftwareTimer0_Handler()
- {
- User_RefreshData();
- User_RefreshAction();
- }
- /**
- * @brief 软件定时器2的更新服务函数
- */
- void User_SoftwareTimer1_Handler()
- {
- ADS1115_RefreshAllChannel();
- }
- /**
- * @brief This function is called by timer update interrupt handler, which will be executed periodically.
- */
- void User_RefreshData()
- {
- for (uint8_t chan = 0; chan < 4; chan++)
- {
- ADC_Voltage[chan] = Filter_MovingAverage(ADS1115_Data[chan]*10,chan)/10.0; //unit: mV, resolution: 0.1 mV
- }
- SensorData[0].value = OmronEC55_PV;
- SensorData[1].value = SGDAQ_PV;
- SensorData[2].value = (float)RS100_Count*0.0005;
- SensorData[3].value = MTSICS_Weight;
- //Pt100 - 1 PV (℃)
- SensorData[4].value = Pt100_RtoT(User_VoltageToResistance(ADC_Voltage[0]));
- SensorData[5].value = ADC_Voltage[1];
- SensorData[6].value = ADC_Voltage[2];
- SensorData[7].value = ADC_Voltage[3];
- //校正后的SGDAQ_PV
- SensorData[8].value = SensorData[1].value * HoldingReg_GetData(30) + HoldingReg_GetData(31);
- }
复制代码
在两个或者多个ADS1115组合使用的情况下,对于多通道数据的采样处理应该遵循与上面相似的策略。以两个ADS1115通过软件模拟I2C组成的8通道数据采集功能为例:
volatile int16_t User_ADS1115Data[8];
- /**
- * @brief 软件定时器2的服务函数
- * 主要实现了两个ADS1115(总共8路)的数据采集功能。
- */
- void User_SoftwareTimer2_Handler()
- {
- static uint8_t chan = 0;
- static uint8_t isData1Copyed = 0;
- static uint8_t isData2Copyed = 0;
- //Operate ADS1115-1
- if(chan < 4)
- {
- if(isData1Copyed == 0)
- {
- //将ADS1115-2采样的数据复制到User_ADS1115Data[]中
- for(uint8_t i = 0; i <4; i++)
- {
- User_ADS1115Data[i+4] = ADS1115_RawData<i>;
- }
- isData1Copyed = 1;
- isData2Copyed = 0;
- }
- //切换I2C总线至ADS1115-1
- I2C_Virtual_SwitchBus(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
- ADS1115_RefreshAllChannel();
- }
- //Operate ADS1115-2
- else
- {
- if(isData2Copyed == 0)
- {
- //将ADS1115-1采样的数据复制到User_ADS1115Data[]中
- for(uint8_t i = 0; i <4; i++)
- {
- User_ADS1115Data<i> = ADS1115_RawData<i>;
- }
- isData2Copyed = 1;
- isData1Copyed = 0;
- }
- //切换I2C总线至ADS1115-2
- I2C_Virtual_SwitchBus(ADS1115_SDA_PORT_2, ADS1115_SDA_PIN_2, ADS1115_SCL_PORT_2, ADS1115_SCL_PIN_2);
- ADS1115_RefreshAllChannel();
- }
- chan++;
- if (chan > 7)
- {
- chan = 0;
- }
- }</i></i></i>
复制代码
使用实时操作系统
在实时操作系统中使用延时函数时,任务调度器会自动切换执行低优先级任务,因此延时函数(由操作系统提供的API)不存在浪费CPU资源的问题。此时一般的做法是建立一个ADS1115的数据采集任务,在这个任务中轮询采集需要的数据。
- void ADS1115Daq_task(void *pvParameters)
- {
- int16_t adcDataTemp[4] = {0};
- TickType_t ticks = xTaskGetTickCount();
- //配置ADS1115端口,由于采用了软件I2C通讯,因此可以直接调用虚拟I2C中的配置函数完成配置
- I2C_Virtual_ConfigPort(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
- I2C_Virtual_SwitchBus(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
-
- //使用内置的快速配置模板完成ADS1115的配置
- ADS1115_UserConfig2();
- while (1)
- {
- for (uint8_t chan = 0; chan < 4; chan++)
- {
- //设置ADS1115的采样通道
- ADS1115_ScanChannel(chan);
- //调用RTOS提供的API延时10ms
- vTaskDelay(10);
- if(ADS1115_ReadRawData(&adcDataTemp[chan])!=0)
- {
- //保留小数点后三位精度
- SensorData[chan].value = (float)(ADS1115_RawDataToVoltage(adcDataTemp[chan])*1000)/1000.0;
- }
- }
- //100ms 一个处理周期
- vTaskDelayUntil( &ticks, 100);
- }
- }
复制代码
|