STM32开发项目:ADS1115的驱动与使用

日期作者版本说明
2020.09.24TaoV1.0发布第一版文档
2020.10.20TaoV1.1改进了void ADS1115_RefreshAllChannel()函数的实现,增加了出错检查机制
2020.11.30TaoV1.2增加了使用RTOS时的ADS1115应用说明

ADS1115介绍

ADS1115是具有 PGA、振荡器、电压基准、比较器的 16 位、860SPS、4 通道 Δ-Σ ADC,数据通过一个 I2C 兼容型串行接口进行传输。有关它的详细说明可以参考官方数据手册

驱动源码

头文件

#ifndef __ADS1115_H__
#define __ADS1115_H__

#include "stm32f4xx.h"
#include "stm32f4xx_conf.h"

#ifdef ADS1115
#include "i2c_virtual.h"

#define ADS1115_SCL_PORT	'A'
#define ADS1115_SCL_PIN		6

#define ADS1115_SDA_PORT	'A'
#define ADS1115_SDA_PIN		5

#define Accuracy 									32768	//ADC单端输入15位精度
#define ADS1115_ADDRESS_0					0x90	//ADDR PIN ->GND
#define ADS1115_ADDRESS_1						0x92	//ADDR PIN ->VDD
#define ADS1115_ADDRESS_2					0x94	//ADDR PIN ->SDA
#define ADS1115_ADDRESS_3					0x96	//ADDR PIN ->SCL

//*************ADDR Initial********************/
#define ADS1115_ADDRESS		ADS1115_ADDRESS_0		//ADDR PIN ->GND
#define ADS1115_ADDRESS_W	ADS1115_ADDRESS|0x00	//write address
#define ADS1115_ADDRESS_R	ADS1115_ADDRESS|0x01	//read address

/************POINTER REGISTER*****************/
#define ADS1115_Pointer_ConverReg			0x00	//Convertion register
#define ADS1115_Pointer_ConfigReg			0x01	//Config register
#define ADS1115_Pointer_LoThreshReg			0x02	//Lo_thresh register
#define ADS1115_Pointer_HiThreshReg			0x03	//Hi_thresh register

/************CONFIG REGISTER*****************/

//Bit[15]
#define ADS1115_OS_OperationalStatus		0x0000		//No Effect
#define ADS1115_OS_SingleConverStart		0x8000		//Begin a single conversion
//Bits[14:12]
#define ADS1115_MUX_Differ_01				0x0000		//AINp=AIN0, AINn=AIN1(default)
#define ADS1115_MUX_Differ_03				0x1000		//AINp=AIN0, AINn=AIN3
#define ADS1115_MUX_Differ_13				0x2000		//AINp=AIN1, AINn=AIN3
#define ADS1115_MUX_Differ_23				0x3000		//AINp=AIN2, AINn=AIN3
#define ADS1115_MUX_Channel_0				0x4000		//AINp=AIN0, AINn=GND
#define ADS1115_MUX_Channel_1				0x5000		//AINp=AIN1, AINn=GND
#define ADS1115_MUX_Channel_2				0x6000		//AINp=AIN2, AINn=GND
#define ADS1115_MUX_Channel_3				0x7000		//AINp=AIN3, AINn=GND
//Bits[11:9]
#define ADS1115_PGA_6144					0x0000		//FS=6.144V
#define ADS1115_PGA_4096					0x0200		//FS=4.096V
#define ADS1115_PGA_2048					0x0400		//FS=2.048V(default)
#define ADS1115_PGA_1024					0x0600		//FS=1.024V
#define ADS1115_PGA_0512					0x0800		//FS=0.512V
#define ADS1115_PGA_0256					0x0A00		//FS=0.256V
//Bit[8]
#define ADS1115_MODE_ContinuConver			0x0000		//Continuous conversion mode
#define ADS1115_MODE_SingleConver			0x0100		//Power-down single-shot mode(default)
//Bits[7:5]
#define ADS1115_DataRate_8					0x0000		//Data Rate = 8
#define ADS1115_DataRate_16					0x0020		//Data Rate = 16
#define ADS1115_DataRate_32					0x0040		//Data Rate = 32
#define ADS1115_DataRate_64					0x0060		//Data Rate = 64
#define ADS1115_DataRate_128				0x0080		//Data Rate = 128(default)
#define ADS1115_DataRate_250				0x00A0		//Data Rate = 250
#define ADS1115_DataRate_475				0x00C0		//Data Rate = 475
#define ADS1115_DataRate_860				0x00E0		//Data Rate = 860
//Bit[4]
#define ADS1115_COMP_MODE_0					0x0000		//Traditional comparator with hysteresis
#define ADS1115_COMP_MODE_1					0x0010		//Window comparator
//Bit[3]
#define ADS1115_COMP_POL_0					0x0000		//Active low
#define ADS1115_COMP_POL_1					0x0008		//Active high
//Bit[2]
#define ADS1115_COMP_LAT_0					0x0000		//Non-latching comparator
#define ADS1115_COMP_LAT_1					0x0004		//Latching comparator
//Bits[1:0]
#define ADS1115_COMP_QUE_0					0x0000		//Assert after one conversion
#define ADS1115_COMP_QUE_1					0x0001		//Assert after two conversion
#define ADS1115_COMP_QUE_2					0x0002		//Assert after four conversion
#define ADS1115_COMP_QUE_3					0x0003		//Disable Comparator

typedef struct
{
    u16 OS;
    u16 MUX;
    u16 PGA;
    u16 MODE;
    u16 DataRate;
    u16 COMP_MODE;
    u16 COMP_POL;
    u16 COMP_LAT;
    u16 COMP_QUE;
} ADS1115_InitTypeDefine;

extern int16_t ADS1115_RawData[4];

void ADS1115_Init();
void ADS1115_UserConfig1();
void ADS1115_UserConfig2();
u8 ADS1115_Config(ADS1115_InitTypeDefine* ADS1115_InitStruct);

u8 ADS1115_ReadRawData(int16_t* rawData);
void ADS1115_ScanChannel(uint8_t channel);
float ADS1115_RawDataToVoltage(int16_t rawData);

float ADS1115_GetVoltage();
float ADS1115_GetAverageVoltage(uint16_t num);

void ADS1115_RefreshAllChannel();

#endif
#endif

源文件

#include "ads1115.h"

#ifdef ADS1115

static ADS1115_InitTypeDefine ADS1115_InitType;

/**
 * 可供外部调用的全局变量,记录了ADS1115采样的原始16位数据。调用void ADS1115_RefreshAllChannel( )函数可以刷新这个变量。
 * 通过定义宏ADS1115_USE_FILTER,可以将ADS1115的轮询采样数据经过滑动滤波后,保存到ADS1115_RawData[]中。
 * 通过float ADS1115_RawDataToVoltage(int16_t rawData)函数可以将ADS1115_RawData[]换算成对应的电压值。
 */
int16_t ADS1115_RawData[4] = {0};


/**
 * @brief 完成芯片控制端口初始化,并设置初始状态
 */
void ADS1115_Init()
{
	I2C_Virtual_ConfigPort(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
}


/**
 * @brief Configuration of ADS1115, single-shot
 */
void ADS1115_UserConfig1()
{
	ADS1115_InitType.COMP_LAT = ADS1115_COMP_LAT_0;
	ADS1115_InitType.COMP_MODE = ADS1115_COMP_MODE_0;
	ADS1115_InitType.COMP_POL = ADS1115_COMP_POL_0;
	ADS1115_InitType.DataRate = ADS1115_DataRate_475;
	ADS1115_InitType.MODE = ADS1115_MODE_SingleConver;
	ADS1115_InitType.MUX = ADS1115_MUX_Channel_0;
	ADS1115_InitType.OS = ADS1115_OS_SingleConverStart;
	ADS1115_InitType.PGA = ADS1115_PGA_4096;

	ADS1115_Config(&ADS1115_InitType);
}


/**
 * @brief Configuration of ADS1115, continuous conversion
 */
void ADS1115_UserConfig2()
{
	ADS1115_InitType.COMP_LAT = ADS1115_COMP_LAT_0;
	ADS1115_InitType.COMP_MODE = ADS1115_COMP_MODE_0;
	ADS1115_InitType.COMP_POL = ADS1115_COMP_POL_0;
	ADS1115_InitType.DataRate = ADS1115_DataRate_475;
	ADS1115_InitType.MODE = ADS1115_MODE_ContinuConver;
	ADS1115_InitType.MUX = ADS1115_MUX_Channel_0;
	ADS1115_InitType.OS = ADS1115_OS_OperationalStatus;
	ADS1115_InitType.PGA = ADS1115_PGA_4096;

	ADS1115_Config(&ADS1115_InitType);
}


/**
 * @brief 配置ADS1115
 * @param ADS1115_InitStruct: 用来配置ADS1115的结构体变量指针
 * @return 配置结果
 * 		@arg: fail
 * 		@arg: success
 */
u8 ADS1115_Config(ADS1115_InitTypeDefine *ADS1115_InitStruct)
{
	u16 Config;
	u8 Writebuff[2];

	Config = ADS1115_InitStruct->OS + ADS1115_InitStruct->MUX + ADS1115_InitStruct->PGA + ADS1115_InitStruct->MODE
			+ ADS1115_InitStruct->DataRate + ADS1115_InitStruct->COMP_MODE + ADS1115_InitStruct->COMP_POL
			+ ADS1115_InitStruct->COMP_LAT + ADS1115_InitStruct->COMP_QUE;

	Writebuff[0] = (unsigned char) ((Config >> 8) & 0xFF);
	Writebuff[1] = (unsigned char) (Config & 0xFF);

	I2C_Virtual_Start();					//启动总线
	I2C_Virtual_SendByte(ADS1115_ADDRESS_W);		//发送器件地址(写)

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_SendByte(ADS1115_Pointer_ConfigReg);		//发送寄存器地址

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_SendByte(Writebuff[0]);		//发送数据

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_SendByte(Writebuff[1]);		//发送数据

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_Stop();

	return (1);
}



/**
 * @brief 读取ADS1115当前通道下的原始数据
 * @param rawData: 传入一个int16_t整型变量的指针,ADS1115的原始数据将保存在这个变量中
 * @return 读取结果
 * 		@arg 0: fail
 * 		@arg 1: success
 */
uint8_t ADS1115_ReadRawData(int16_t *rawData)
{
	unsigned char Result[2];

	I2C_Virtual_Start();															//启动总线
	I2C_Virtual_SendByte(ADS1115_ADDRESS_W);					//发送器件地址(写)

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_SendByte(ADS1115_Pointer_ConverReg); 	//发送寄存器地址

	if (I2C_Virtual_ack == 0)
		return (0);

	I2C_Virtual_Stop();
	delay_us(10);
	I2C_Virtual_Start();											//写寄存器之后需要重新启动总线

	I2C_Virtual_SendByte(ADS1115_ADDRESS_R);						//发送器件地址(读)

	if (I2C_Virtual_ack == 0)
		return (0);

	Result[0] = I2C_Virtual_RcvByte();		//接收数据
	I2C_Virtual_Ack();			//发送就答位
	Result[1] = I2C_Virtual_RcvByte();
	I2C_Virtual_NoAck();			//发送非应位
	I2C_Virtual_Stop(); 			//结束总线

	*rawData = (int16_t) (((Result[0] << 8) & 0xFF00) | (Result[1] & 0xFF));

	return 1;
}


/**
 * @brief Switch the channel of ADS1115
 * @param channel
 */
void ADS1115_ScanChannel(uint8_t channel)
{
	switch (channel)
	{
	case 0:
		ADS1115_InitType.MUX = ADS1115_MUX_Channel_0;
		break;
	case 1:
		ADS1115_InitType.MUX = ADS1115_MUX_Channel_1;
		break;
	case 2:
		ADS1115_InitType.MUX = ADS1115_MUX_Channel_2;
		break;
	case 3:
		ADS1115_InitType.MUX = ADS1115_MUX_Channel_3;
		break;
	default:
		break;
	}

	ADS1115_Config(&ADS1115_InitType);
}


/**
 * @brief 将传感器的原始采样数据转化为电压数据,
 * 			根据ADS1115_InitType结构体中包含的增益信息计算
 * @param rawData: 待转换的原始数据
 * @retval 返回经过计算的电压值
 */
float ADS1115_RawDataToVoltage(int16_t rawData)
{
	float voltage;

	switch (ADS1115_InitType.PGA)
	{
	case ADS1115_PGA_0256:
		voltage = rawData * 0.0078125;
		break;

	case ADS1115_PGA_0512:
		voltage = rawData * 0.015625;
		break;

	case ADS1115_PGA_1024:
		voltage = rawData * 0.03125;
		break;

	case ADS1115_PGA_2048:
		voltage = rawData * 0.0625;
		break;

	case ADS1115_PGA_4096:
		voltage = rawData * 0.125;
		break;

	case ADS1115_PGA_6144:
		voltage = rawData * 0.1875;
		break;

	default:
		voltage = 0;
		break;
	}

	return voltage;
}


/**
 * @brief 直接获取ADS1115当前通道的电压采样值
 * @return 电压采样值
 */
float ADS1115_GetVoltage()
{
	int16_t rawData;

	ADS1115_ReadRawData(&rawData);

	return ADS1115_RawDataToVoltage(rawData);
}


/**
 * @brief 获取并计算ADC采样的平均电压值
 * @param num: 计算平均值的数量
 * @retval 电压采样的平均值
 */
float ADS1115_GetAverageVoltage(uint16_t num)
{
	int32_t sum = 0;
	int16_t rawData;

	if(num == 0)
	{
		return ADS1115_GetVoltage( );
	}

	for(uint16_t i =0; i< num;i++)
	{
		ADS1115_ReadRawData(&rawData);
		sum += rawData;
	}

	return ADS1115_RawDataToVoltage(sum/num);
}

/**
 * @brief 刷新ADS1115全部通道的采样数据
 * 		由于ADS1115通道切换后需要等待较长时间数据才能够稳定,
 * 		在进行多路数据采集的时候,切换通道后延时阻塞等待切换完成会占用过多的系统时间,
 * 		因此需要在一个定时器中轮询采集ADS1115数据,每次采集完成后,切换到下一个通道
 * 		大幅度提高了系统工作的效率。
 *
 * 		调用此函数可以刷新全局变量ADS1115_RawData[4]的值。
 *
 * 		应当在一个定时器更新中断服务函数中周期性的调用此函数,更新周期最好小于200Hz
 */
void ADS1115_RefreshAllChannel()
{
	static uint8_t channel = 0;
	int16_t adcDataTemp = 0;

	//通道切换时可能有不确定的数据读出,因此需要将前1~2次读出的数据舍弃
	ADS1115_ReadRawData(&adcDataTemp);
	ADS1115_ReadRawData(&adcDataTemp);

	//读取数据返回正确,则将读到的数据写入ADS1115_RawData数组中
	if( ADS1115_ReadRawData(&adcDataTemp) !=0 )
	{
		ADS1115_RawData[channel] = adcDataTemp;
	}
	
	//ADS1115总共4个通道
	channel++;
	
	if(channel>ADS1115_MAX_CHANNEL-1)
		channel = 0;

	//结束采样后切换至下一通道
	ADS1115_ScanChannel(channel);
}
#endif

使用指南

基本步骤

  1. 初始化软件模拟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);
  1. 初始化ADS1115芯片的配置。笔者在驱动库中提供了两种初始化配置函数void ADS1115_UserConfig1()void ADS1115_UserConfig2(),可以修改这两个函数中的配置参数后直接调用。
	ADS1115_UserConfig2();
  1. 设置或者切换ADS1115的通道(配置完ADS1115之后,可以设置一个初始通道)。
	ADS1115_ScanChannel(1);
  1. 根据项目的需要,选择采集原始数据然后在其他地方换算成电压数据,或者直接采集电压数据以及滤波后的电压数据。
	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;
	}
}

使用实时操作系统

在实时操作系统中使用延时函数时,任务调度器会自动切换执行低优先级任务,因此延时函数(由操作系统提供的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);
	}
}
  • 40
    点赞
  • 218
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全能骑士涛锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值