STM32开发项目:ADS1115的驱动与使用
日期 | 作者 | 版本 | 说明 |
---|---|---|---|
2020.09.24 | Tao | V1.0 | 发布第一版文档 |
2020.10.20 | Tao | V1.1 | 改进了void ADS1115_RefreshAllChannel()函数的实现,增加了出错检查机制 |
2020.11.30 | Tao | V1.2 | 增加了使用RTOS时的ADS1115应用说明 |
目录
- 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 Comparatortypedef 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 ADS1115static 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
使用指南
基本步骤
- 初始化软件模拟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_UserConfig2();
- 设置或者切换ADS1115的通道(配置完ADS1115之后,可以设置一个初始通道)。
ADS1115_ScanChannel(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 filterADC_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的原始采样数据(换算成电压、滤波、校正等)与其他传感器的数据。
- 通过多个更新周期不同的定时器的配合,可以在无代码延时的情况下解决不同传感器数据采集速度不一致的问题,周期性的调用代替了延时等待。
- 软件定时器1以40Hz的频率周期性的调用
/*** @brief 设置软件定时器,主要包括初始化与开启各个软件定时器*/
void User_SetupTimer()
{//Poll frequency of software timer is 1KHz//SW_Timer0 for refresh sensor data and modbus action. Freq. = 10HzSoftwareTimer_Init(0, 100, User_SoftwareTimer0_Handler, -1);//SW_Timer1 for ads1115 data acquisition. Freq. = 40HzSoftwareTimer_Init(1, 25, User_SoftwareTimer1_Handler, -1);//SW_Timer2 for RS485 scan. Freq. = 10HzSoftwareTimer_Init(2, 100, User_SoftwareTimer2_Handler, -1);//SW_Timer3 for MTSICS. Freq. = 10HzSoftwareTimer_Init(3, 100, User_SoftwareTimer3_Handler, -1);//SW_Timer4 for RS100 (Printer has not been used). Freq. = 10HzSoftwareTimer_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_PVSensorData[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-1if(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-1I2C_Virtual_SwitchBus(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);ADS1115_RefreshAllChannel();}//Operate ADS1115-2else{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-2I2C_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延时10msvTaskDelay(10);if(ADS1115_ReadRawData(&adcDataTemp[chan])!=0){//保留小数点后三位精度SensorData[chan].value = (float)(ADS1115_RawDataToVoltage(adcDataTemp[chan])*1000)/1000.0;}}//100ms 一个处理周期vTaskDelayUntil( &ticks, 100);}
}
STM32开发项目:ADS1115的驱动与使用相关推荐
- STM32开发项目:一些模拟电子开关的介绍与驱动
日期 作者 版本 说明 2020.09.29 Tao V1.0 撰写了ADG408.DG467芯片的简介与驱动 2020.09.30 Tao V1.1 撰写了WAS3157B芯片的简介与驱动 2020 ...
- STM32开发项目:借助DMA传输高效的使用ADC
目录 项目背景 DMA简介 DMA请求 DMA通道 DMA仲裁 DMA配置 从哪里来到哪里去 要传多少,单位是什么 什么时候传输完成 代码示例 配置GPIO 相关变量定义 配置ADC 配置DMA 获取 ...
- STM32开发项目:定时器预装载寄存器(ARR)
项目背景 笔者在进行某项目开发时,需要实时调节定时器的周期,以控制定时器下次更新中断的时间. 预装载寄存器 自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值.当计数 ...
- STM32开发项目:STM32F407的BootLoader程序
日期 作者 版本 说明 2020.11.03 Tao V0.0 完成主体内容的撰写 目录 BootLoader程序介绍 源码实现 源文件 stm32f4_flash.ld 使用指南 设置用户程序下载位 ...
- STM32开发项目:GPIO的位带操作
目录 背景介绍 GPIO位带操作的实现 使用指南 背景介绍 位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见.51 单片机中通过关键字 sbit 来实现位定义,STM32-M3, ...
- 免费分享嵌入式stm32基础项目开发:心率检测仪的设计与实现
本教程主要给大家谅解了嵌入式stm32开发 心率检测仪的设计与实现,需要的朋友们可以下载来看看,作为参考! 项目描述:通过心律传感器采集我们的心律数据,然后通过串口传送到上位机中,上位机用Qt实现,当 ...
- 嵌入式stm32基础项目开发:心率检测仪的设计与实现
嵌入式stm32基础项目开发:心率检测仪的设计与实现 本教程主要给大家谅解了嵌入式stm32开发 心率检测仪的设计与实现,需要的朋友们可以下载来看看,作为参考! 项目描述:通过心律传感器采集我们的心律 ...
- STM32开发入门及实战 (1)
本博客的编写目的: 一.自我总结,记录. 二.分享,输出,加深思考. 三.不作细致如书本般编排,尽管那样的排版很好看,但是过于耗费时间,还有很多东西没有必要说明,完全可以自己去解决,但还是尽量做好排版 ...
- STM32开发环境(工具)之Keil MDK 介绍
STM32微处理器基于ARM核,所以很多基于ARM嵌入式开发环境都可用于STM32开发平台.开发工具都可用于STM32开发.选择合适的开发环境可以加快开发进度,节省开发成本.本章将先对STM32常用的 ...
- GC9A01-TFT屏幕驱动(整理有stm32/51单片机/arduino等驱动代码)
GC9A01-TFT屏幕驱动 & 整理有stm32/51单片机/arduino等驱动代码 前言 关于GC9A01 stm32驱动 引脚接线 代码移植 文件复制 端口修改 显示函数 中文汉字数组 ...
最新文章
- Mcad学习笔记之序列化(2进制和Soap序列化)
- Bluetooth profile: ATT/GATT(襄坤在线)
- python 设置图片x轴带单位_用Python帮你上马,哪里无码打哪里
- HTTP中常用响应头
- java 8 new feature_java 8 new feature --- default method
- win10锁定计算机会断网吗,Win10专业版如何设置锁屏后不断网?超详细的图文教程...
- python除法运算定律_除法竖式算法的原理是什么?
- SQL Management Studio Express 安装缺少MSXML6解决
- 计算机IP掩码的与运算,计算机IP地址与子网掩码如何进行AND运算
- 【HDOJ6071】Lazy Running(同余最短路思想)
- 经纬度十进制与度分秒换算及数据库实现
- Ubuntu磁盘分区和挂载
- iOS Extension调试 无法在Xcode上进行调试
- mysql查看占用内存或者CPU高的SQL
- (第二章)HTML基本标记
- 我的世界服务器如何修改天气,我的世界怎么切换天气 原来这么简单
- 薪资大曝光,转行测试工程师:真香!!!
- Win10家庭中文版用批处理打开本地组策略
- 金蝶kis记账王报表应用方法
- springboot+神奇桔乡旅游信息系统 毕业设计-附源码191750