snprintf/_snprintf 在不同平台间函数差异

文章一:http://www.cppblog.com/sandy/archive/2006/09/29/13133.html

谈谈snprintf

 

众所周知,sprintf不能检查目标字符串的长度,可能造成众多安全问题,所以都会推荐使用snprintf.

snprintf(_snprintf)的声明是这样的

int _snprintf(
   char *buffer,
   size_t count,
   const char *format [,
      argument] ...
);

If len < count, then len characters are stored in buffer, a null-terminator is appended, and len is returned.

If len = count, then len characters are stored in buffer, no null-terminator is appended, and len is returned.

If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned.


最常见的错误用法有:
1.
char sa[256]={0};
_snprintf(sa,sizeof(sa),"%s",sb);
//错误原因:当sb的长度>=256的时候,sa将没有'/0'结尾

2.
char sa[256];
_snprintf(sa,sizeof(sa)-1,"%s",sb);
//错误原因:当sb的长度>=255的时候,sa将没有'/0'结尾,忘记给sa初始化

3.
char sa[256];
_snprintf(sa,sizeof(sa)-1,"%s",sb);
sa[sizeof(sa)]=0;
//错误原因:最后一行数组越界

正确的用法
1. //推荐用法
char sa[256];
sa[sizeof(sa)-1]=0;
_snprintf(sa,sizeof(sa),"%s",sb);
if(sa[sizeof(sa)-1]!=0)
{
   printf("warning:string will be truncated");
   sa[sizeof(sa)-1]=0;
}

2.
char sa[256]={0};
int result = _snprintf(sa,sizeof(sa),"%s",sb);
if(result==sizeof(sa) || result<0)
{
    printf("warning:sting will be truncated");
   sa[sizeof(sa)-1]=0;
}

Windows:

char sa[2];
char sb[3]="aa";
_snprintf(sa, sizeof(sa), "%s/0", sb);

==> sa="aa",但是没有'/0'结束

 

Linux:

char sa[2];
char sb[3]="aa";
snprintf(sa, sizeof(sa), "%s", sb);
这样用的, 而且sa=>"a", 系统会自动截断

 

 

文章二:snprintf/_snprintf 在不同平台间函数差异

http://hi.baidu.com/xinzsky/blog/item/5e03981f828c9bf6e1fe0bf3.html

 

 

2007-11-20 17:25

snprintf函数并不是标准c/c++中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。
在gcc中,该函数名称就snprintf,而在VC中称为_snprintf。
  由于不是标准函数,没有一个统一的标准来规定该函数的行为,所以导致了各厂商间的实现版本可
能会有差异。今天也的的确确看到了差异,因为这个小小的差异是我的程序无法正常的处理数据。
  这个小小的差异发生在count参数。在VC中,这个count就是要写入的总字符串字符数,例如:
    
//VC
int main(int argc, char * argv[])
...
{
    
char   buff[100
];
     printf(
"%d ",_snprintf(buff,10,"1234567890ab"
));
     printf(
"%s"
,buff);
    
return 0
;
}


//Linxu:gcc/g++
#include <stdio.h>
int main(int argc, char * argv[])
...
{
    
char   buff[100
];
     printf(
"%d ",snprintf(buff,10,"1234567890ab"
));
     printf(
"%s"
,buff);
    
return 0
;
}
 vc程序的输出是:
-1
1234567890@
gcc程序的输出是:
12
123456789
从输出结果可以知道:VC中的_snprintf的count参数表示,会向buff中写入count个字符,不包括'/0'字符,
并且不会在字符串末尾添加'/0'符,并且,如果字符串超过count,函数返回-1以标志可能导致的错误;gcc
中的snprintf函数的count参数表示,向buff中写入10个字符,包括'/0'字符,并且,返回实际的字符串长度,
例子中为12。
  如果不了解这些函数在各平台间的差异,也许我们的程序在移植过程中会变得很脆弱。我们应该小心各种各样
的陷阱。

下面是MSDN对_snprintf函数的解释:

int _snprintf( char *buffer, size_t count, const char *format [, argument] ... );
Parameters
buffer
Storage location for output
count
Maximum number of characters to store
format
Format-control string
argument
Optional arguments
Libraries

All versions of the C run-time libraries.

Return Value

_snprintf returns the number of bytes stored in buffer, not counting the terminating null character. If the number of bytes required to store the data exceeds count, then count bytes of data are stored in buffer and a negative value is returned.

Remarks

The _snprintf function formats and stores count or fewer characters and values (including a terminating null character that is always appended unless count is zero or the formatted string length is greater than or equal to count characters) in buffer. Each argument (if any) is converted and output according to the corresponding format specification in format. The format consists of ordinary characters and has the same form and function as the format argument for printf. If copying occurs between strings that overlap, the behavior is undefined.

 

下面是我写的代码,你分析一下什么原因#include "adc.h" #include "./SYSTEM/delay/delay.h" #include <math.h> ADC_HandleTypeDef g_adc_handle; /* ADC句柄 */ ///* ADC初始化函数,本函数支持ADC1/ADC2任意通道, 但是不支持ADC3 */ //void adc_init(void) //{ // g_adc_handle.Instance = ADC_ADCX; /* 选择哪个ADC */ // g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */ // g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式,仅用到一个通道 */ // g_adc_handle.Init.ContinuousConvMode = DISABLE; /* 关闭连续转换模式 */ // g_adc_handle.Init.NbrOfConversion = 1; /* 赋值范围是1~16,本实验用到1个规则通道序列 */ // g_adc_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组断模式 */ // g_adc_handle.Init.NbrOfDiscConversion = 0; /* 配置断模式的规则通道个数,禁止规则通道组断模式后,此参数忽略 */ // g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */ // HAL_ADC_Init(&g_adc_handle); /* 初始化 */ // HAL_ADCEx_Calibration_Start(&g_adc_handle); /* 校准ADC */ //} ///* ADC底层驱动,引脚配置,时钟使能 */ //void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc) //{ // if(hadc->Instance == ADC_ADCX) // { // GPIO_InitTypeDef gpio_init_struct; // RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // // ADC_ADCX_CHY_CLK_ENABLE(); /* 使能ADCx时钟 */ // ADC_ADCX_CHY_GPIO_CLK_ENABLE(); /* 开启GPIO时钟 */ // /* 设置ADC时钟 */ // adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */ // adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */ // HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */ // /* 设置AD采集通道对应IO引脚工作模式 */ // gpio_init_struct.Pin = ADC_ADCX_CHY_GPIO_PIN; /* ADC通道IO引脚 */ // gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */ // HAL_GPIO_Init(ADC_ADCX_CHY_GPIO_PORT, &gpio_init_struct); // } //} ///** // * @brief 设置ADC通道采样时 // * @param adcx : adc句柄指针,ADC_HandleTypeDef // * @param ch : 通道号, ADC_CHANNEL_0~ADC_CHANNEL_17 // * @param stime: 采样时 0~7, 对应关系为: // * @arg ADC_SAMPLETIME_1CYCLE_5, 1.5个ADC时钟周期 ADC_SAMPLETIME_7CYCLES_5, 7.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_13CYCLES_5, 13.5个ADC时钟周期 ADC_SAMPLETIME_28CYCLES_5, 28.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_41CYCLES_5, 41.5个ADC时钟周期 ADC_SAMPLETIME_55CYCLES_5, 55.5个ADC时钟周期 // * @arg ADC_SAMPLETIME_71CYCLES_5, 71.5个ADC时钟周期 ADC_SAMPLETIME_239CYCLES_5, 239.5个ADC时钟周期 // * @param rank: 多通道采集时需要设置的采集编号, // 假设你定义channle1的rank=1,channle2 的rank=2, // 那么对应你在DMA缓存空的变量数组AdcDMA[0] 就i是channle1的转换结果,AdcDMA[1]就是通道2的转换结果。 // 单通道DMA设置为 ADC_REGULAR_RANK_1 // * @arg 编号1~16:ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16 // * @retval 无 // */ //void adc_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime) //{ // ADC_ChannelConfTypeDef adc_ch_conf; // // adc_ch_conf.Channel = ch; /* 通道 */ // adc_ch_conf.Rank = rank; /* 序列 */ // adc_ch_conf.SamplingTime = stime; /* 采样时 */ // HAL_ADC_ConfigChannel(adc_handle, &adc_ch_conf); /* 通道配置 */ //} ///** // * @brief 获得ADC转换后的结果 // * @param ch: 通道值 0~17,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_17 // * @retval 无 // */ //uint32_t adc_get_result(uint32_t ch) //{ // adc_channel_set(&g_adc_handle , ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5); /* 设置通道,序列采样时 */ // HAL_ADC_Start(&g_adc_handle); /* 开启ADC */ // HAL_ADC_PollForConversion(&g_adc_handle, 20); /* 轮询转换 */ // return (uint16_t)HAL_ADC_GetValue(&g_adc_handle); /* 返回最近一次ADC1规则组的转换结果 */ //} ///** // * @brief 获取通道ch的转换值,取times次,然后平均 // * @param ch : 通道号, 0~17 // * @param times : 获取次数 // * @retval 通道ch的times次转换结果平均值 // */ //uint32_t adc_get_result_average(uint32_t ch, uint8_t times) //{ // uint32_t temp_val = 0; // uint8_t t; // for (t = 0; t < times; t++) /* 获取times次数据 */ // { // temp_val += adc_get_result(ch); // delay_ms(5); // } // return temp_val / times; /* 返回平均值 */ //} /*************************单通道ADC采集(DMA读取)实验多通道ADC采集(DMA读取)实验公用代码*******************************/ DMA_HandleTypeDef g_dma_adc_handle = {0}; // 定义要搬运ADC数据的DMA句柄 ADC_HandleTypeDef g_adc_dma_handle = {0}; // 定义ADC(DMA读取)句柄 uint8_t g_adc_dma_sta = 0; // DMA传输状态标志, 0,未完成; 1, 已完成 DMA_HandleTypeDef g_dma_nch_adc_handle = {0}; /* 定义要搬运ADC多通道数据的DMA句柄 */ ADC_HandleTypeDef g_adc_nch_dma_handle = {0}; /* 定义ADC(多通道DMA读取)句柄 */ /** * @brief ADC N通道(6通道) DMA读取 初始化函数 * @note 本函数还是使用adc_init对ADC进行大部分配置,有差异的地方再单独配置 * 另外,由于本函数用到了6个通道, 宏定义会比较多内容, 因此,本函数就不采用宏定义的方式来修改通道了, * 直接在本函数里面修改, 这里我们默认使用PA0~PA5这6个通道. * * 注意: 本函数还是使用 ADC_ADCX(默认=ADC1) ADC_ADCX_DMACx( DMA1_Channel1 ) 及其相关定义 * 不要乱修改adc.h里面的这两部分内容, 必须在理解原理的基础上进行修改, 否则可能导致无法正常使用. * * @param mar : 存储器地址 * @retval 无 */ void adc_nch_dma_init(uint32_t mar) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; ADC_ChannelConfTypeDef adc_ch_conf = {0}; ADC_ADCX_CHY_CLK_ENABLE(); /* 使能ADCx时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 开启GPIOA时钟 */ if ((uint32_t)ADC_ADCX_DMACx > (uint32_t)DMA1_Channel7) /* 大于DMA1_Channel7, 则为DMA2的通道了 */ { __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */ } else { __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */ } /* 设置ADC时钟 */ adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; /* ADC外设时钟 */ adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; /* 分频因子6时钟为72M/6=12MHz */ HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); /* 设置ADC时钟 */ /* 设置ADC1通道0~5对应的IO口模拟输入 AD采集引脚模式设置,模拟输入 PA1对应 ADC1_IN1 PA2对应 ADC1_IN2 PA3对应 ADC1_IN3 */ gpio_init_struct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; /* GPIOA1-3 */ gpio_init_struct.Mode = GPIO_MODE_ANALOG; /* 模拟 */ HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 初始化DMA */ g_dma_nch_adc_handle.Instance = ADC_ADCX_DMACx; /* 设置DMA通道 */ g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* 从外设到存储器模式 */ g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设非增量模式 */ g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器增量模式 */ g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外设数据长度:16 */ g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* 存储器数据长度:16 */ g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL; /* 外设流控模式 */ g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM; /* 中等优先级 */ HAL_DMA_Init(&g_dma_nch_adc_handle); __HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle); /* 将DMA与adc联系起来 */ /* 初始化ADC */ g_adc_nch_dma_handle.Instance = ADC_ADCX; /* 选择哪个ADC */ g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; /* 数据对齐方式:右对齐 */ g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE; /* 使能扫描模式 */ g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE; /* 使能连续转换 */ g_adc_nch_dma_handle.Init.NbrOfConversion = 3; /* 赋值范围是1~16,本实验用到3个规则通道序列 */ g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE; /* 禁止规则通道组断模式 */ g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0; /* 配置断模式的规则通道个数,禁止规则通道组断模式后,此参数忽略 */ g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 软件触发 */ HAL_ADC_Init(&g_adc_nch_dma_handle); /* 初始化 */ HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle); /* 校准ADC */ /* 配置ADC通道 */ adc_ch_conf.Channel = ADC_CHANNEL_1; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_1; /* 采样序列里的第1个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 通道配置 */ adc_ch_conf.Channel = ADC_CHANNEL_2; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_2; /* 采样序列里的第2个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 配置ADC通道 */ adc_ch_conf.Channel = ADC_CHANNEL_3; /* 配置使用的ADC通道 */ adc_ch_conf.Rank = ADC_REGULAR_RANK_3; /* 采样序列里的第3个 */ adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ; /* 采样时,设置最大采样周期:239.5个ADC周期 */ HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); /* 配置ADC通道 */ /* 配置DMA数据流请求中断优先级 */ HAL_NVIC_SetPriority(ADC_ADCX_DMACx_IRQn, 3, 3); HAL_NVIC_EnableIRQ(ADC_ADCX_DMACx_IRQn); HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0); /* 启动DMA,并开启中断 */ HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar, 0); /* 开启ADC,通过DMA传输结果 */ } /*************************单通道ADC采集(DMA读取)实验多通道ADC采集(DMA读取)实验公用代码*******************************/ /** * @brief 使能一次ADC DMA传输 * @note 该函数用寄存器来操作,防止用HAL库操作对其他参数有修改,也为了兼容性 * @param ndtr: DMA传输的次数 * @retval 无 */ void adc_dma_enable(uint16_t cndtr) { ADC_ADCX->CR2 &= ~(1 << 0); /* 先关闭ADC */ ADC_ADCX_DMACx->CCR &= ~(1 << 0); /* 关闭DMA传输 */ while (ADC_ADCX_DMACx->CCR & (1 << 0)); /* 确保DMA可以被设置 */ ADC_ADCX_DMACx->CNDTR = cndtr; /* DMA传输数据量 */ ADC_ADCX_DMACx->CCR |= 1 << 0; /* 开启DMA传输 */ ADC_ADCX->CR2 |= 1 << 0; /* 重新启动ADC */ ADC_ADCX->CR2 |= 1 << 22; /* 启动规则转换通道 */ } /** * @brief ADC DMA采集中断服务函数 * @param 无 * @retval 无 */ void ADC_ADCX_DMACx_IRQHandler(void) { if (ADC_ADCX_DMACx_IS_TC()) { g_adc_dma_sta = 1; /* 标记DMA传输完成 */ ADC_ADCX_DMACx_CLR_TC(); /* 清除DMA1 数据流7 传输完成中断 */ } },main.c代码#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/DMA/dma.h" #include "./BSP/KEY/key.h" #include "./BSP/ADC/adc.h" #include "OLED.h" // 重新排序全局变量定义,确保阈值变量在ADC缓冲区之前 volatile float MQ7_ALARM_THRESHOLD = 7.0; // 添加volatile防止优化 volatile float MQ8_ALARM_THRESHOLD = 20.0; volatile float MP2_ALARM_THRESHOLD = 15.0; // 添加全局变量存储基线值 float baseline_values[3] = {0}; extern const uint8_t HZ_zhong[]; // 添加内存保护屏障 #define ADC_DMA_BUF_SIZE (3 * 50) /* ADC DMA缓冲区大小 */ uint32_t __attribute__((aligned(4))) memory_barrier[4]; // 内存屏障 static uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA缓冲区 */ extern uint8_t g_adc_dma_sta; /* DMA传输完成标志 */ // 汉字点阵数据 const uint8_t HZ_guo[] = { 0x00,0xFE,0xFE,0x12,0x92,0x92,0x92,0xF2, 0xF2,0x92,0x92,0x92,0x12,0xFE,0xFE,0x00, 0x00,0xFF,0xFF,0x48,0x48,0x48,0x48,0x4F, 0x4F,0x4A,0x4E,0x4C,0x48,0xFF,0xFF,0x00 }; const uint8_t HZ_nan[] = { 0x04,0xE4,0xE4,0x24, 0x64,0xE4,0xA4,0x3F, 0x3F,0xA4,0xE4,0x64, 0x24,0xE4,0xE4,0x04, 0x00,0xFF,0xFF,0x08, 0x09,0x09,0x09,0x7F, 0x7F,0x09,0x09,0x49, 0xC8,0xFF,0x7F,0x00,/*"南",0*/ }; const uint8_t HZ_fang[] = { 0x08,0x08,0x08,0x08, 0x08,0xF8,0xF9,0x8F, 0x8E,0x88,0x88,0x88, 0x88,0x08,0x08,0x08, 0x00,0x80,0xC0,0x60, 0x38,0x1F,0x07,0x00, 0x40,0xC0,0xC0,0x7F, 0x3F,0x00,0x00,0x00,/*"方",0*/ }; const uint8_t HZ_dian[] = { 0x00,0x00,0xF8,0xF8, 0x88,0x88,0x88,0xFF, 0xFF,0x88,0x88,0x88, 0xF8,0xF8,0x00,0x00, 0x00,0x00,0x1F,0x1F, 0x08,0x08,0x08,0x7F, 0xFF,0x88,0x88,0x88, 0x9F,0x9F,0xF0,0xF0,/*"电",0*/ }; const uint8_t HZ_wang[] = { 0x00,0xFE,0xFE,0x22, 0x62,0xC2,0xF2,0x72, 0x22,0x62,0xC2,0xF2, 0x72,0xFE,0xFE,0x00, 0x00,0xFF,0xFF,0x18, 0x0E,0x07,0x0F,0x1E, 0x18,0x0E,0x07,0x4F, 0xCE,0xFF,0x7F,0x00,/*"网",0*/ }; ////////////////////////////////////// 按键处理优化 /////////////////////////////////// int selected_sensor = 0; // 当前选择的传感器 // 按键状态结构体 typedef struct { uint32_t last_time; // 上次检测时 uint8_t last_state; // 上次状态 uint8_t pressed; // 按下标志 } KeyState; KeyState xz_key = {0, 1, 0}; KeyState key0_key = {0, 1, 0}; KeyState key1_key = {0, 1, 0}; // 更新阈值函数(带范围限制) void update_threshold(float delta) { switch (selected_sensor) { case 0: MQ7_ALARM_THRESHOLD += delta; if (MQ7_ALARM_THRESHOLD < 0) MQ7_ALARM_THRESHOLD = 0; if (MQ7_ALARM_THRESHOLD > 100) MQ7_ALARM_THRESHOLD = 100; break; case 1: MQ8_ALARM_THRESHOLD += delta; if (MQ8_ALARM_THRESHOLD < 0) MQ8_ALARM_THRESHOLD = 0; if (MQ8_ALARM_THRESHOLD > 100) MQ8_ALARM_THRESHOLD = 100; break; case 2: MP2_ALARM_THRESHOLD += delta; if (MP2_ALARM_THRESHOLD < 0) MP2_ALARM_THRESHOLD = 0; if (MP2_ALARM_THRESHOLD > 100) MP2_ALARM_THRESHOLD = 100; break; } } // 更新按键状态 void update_key_state(KeyState *key, GPIO_PinState current_state) { uint32_t now = HAL_GetTick(); uint8_t current_pin_state = (current_state == GPIO_PIN_SET) ? 1 : 0; // 检测下降沿(按键按下) if (key->last_state == 1 && current_pin_state == 0) { if ((now - key->last_time) > 20) { // 20ms消抖 key->pressed = 1; } } // 更新状态 key->last_state = current_pin_state; key->last_time = now; } // 处理按键逻辑 void process_keys(void) { // 更新按键状态 update_key_state(&xz_key, HAL_GPIO_ReadPin(XZ_GPIO_PORT, XZ_GPIO_PIN)); update_key_state(&key0_key, HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN)); update_key_state(&key1_key, HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN)); // 处理XZ按键(选择传感器) if (xz_key.pressed) { selected_sensor = (selected_sensor + 1) % 3; xz_key.pressed = 0; } // 处理KEY0按键(增加阈值) if (key0_key.pressed) { update_threshold(1.0f); key0_key.pressed = 0; } // 处理KEY1按键(减少阈值) if (key1_key.pressed) { update_threshold(-1.0f); key1_key.pressed = 0; } } /////////////////////////////////////////////////////////////////////////////////// // 显示电压值(清除残留字符) void display_voltage_value(uint8_t x, uint8_t y, float voltage) { char buffer[10]; // 清除旧内容(5宽度,包括小数点小数) OLED_ShowString(x, y, " ", 8); // 显示新值 snprintf(buffer, sizeof(buffer), "%.0f", voltage); OLED_ShowString(x, y, buffer, 8); } // 显示阈值值(清除残留字符) void display_threshold_value(uint8_t x, uint8_t y, float threshold) { char buffer[10]; // 清除旧内容(4宽度,包括小数点小数) OLED_ShowString(x, y, " ", 8); // 显示新值 snprintf(buffer, sizeof(buffer), "%.1f", threshold); OLED_ShowString(x, y, buffer, 8); } int main(void) { // float mq7_concentration, mq8_concentration, mp2_concentration; HAL_Init(); sys_stm32_clock_init(RCC_PLL_MUL9); delay_init(72); usart_init(115200); led_init(); key_init(); OLED_Init(); OLED_Clear(); // 开机动画 OLED_ShowChinese(2, 16, HZ_zhong); OLED_ShowChinese(21, 16, HZ_guo); OLED_ShowChinese(40, 16, HZ_nan); OLED_ShowChinese(59, 16, HZ_fang); OLED_ShowChinese(78, 16, HZ_dian); OLED_ShowChinese(97, 16, HZ_wang); OLED_UpData(); delay_ms(1000); OLED_Clear(); // 添加变量用于存储每个通道的ADC值电压 uint16_t adc_value[3] = {0}; float voltage[3] = {0.0f}; // 初始化ADC DMA adc_nch_dma_init((uint32_t)g_adc_dma_buf); adc_dma_enable(ADC_DMA_BUF_SIZE); // 显示初始静态文本 OLED_ShowString(1, 1, "CO:", 8); OLED_ShowString(1, 16, "CO2:", 8); OLED_ShowString(1, 32, "CH4:", 8); // 初始显示阈值(使用清除残留字符的函数) display_threshold_value(90, 1, MQ7_ALARM_THRESHOLD); display_threshold_value(90, 16, MQ8_ALARM_THRESHOLD); display_threshold_value(90, 32, MP2_ALARM_THRESHOLD); // 初始选择标记 selected_sensor = 0; OLED_ShowString(80, 1, "*", 8); // 初始选择第一个传感器 OLED_UpData(); delay_ms(100); while (1) { // 处理按键 process_keys(); // 更新选择标记 - 先清除所有标记 OLED_ShowString(80, 1, " ", 8); OLED_ShowString(80, 16, " ", 8); OLED_ShowString(80, 32, " ", 8); // 设置当前选中标记 switch (selected_sensor) { case 0: OLED_ShowString(80, 1, "*", 8); break; case 1: OLED_ShowString(80, 16, "*", 8); break; case 2: OLED_ShowString(80, 32, "*", 8); break; } if (g_adc_dma_sta == 1) { // 处理DMA缓冲区数据 for (uint8_t j = 0; j < 3; j++) { uint32_t sum = 0; // 计算该通道的平均值 for (uint16_t i = 0; i < (ADC_DMA_BUF_SIZE / 3); i++) { sum += g_adc_dma_buf[(3 * i) + j]; } // 存储平均值 adc_value[j] = sum / (ADC_DMA_BUF_SIZE / 3); switch(j) { case 0: display_voltage_value(35, 1, adc_value[0] ); break; case 1: display_voltage_value(35, 16, adc_value[1] ); break; case 2: display_voltage_value(35, 32, adc_value[2] ); break; } } display_threshold_value(90, 1, MQ7_ALARM_THRESHOLD); display_threshold_value(90, 16, MQ8_ALARM_THRESHOLD); display_threshold_value(90, 32, MP2_ALARM_THRESHOLD); g_adc_dma_sta = 0; adc_dma_enable(ADC_DMA_BUF_SIZE); } LED0_TOGGLE(); OLED_UpData(); delay_ms(10); } },用的是STM32F103的M48Z-M3的单片机开发板,出现了使用多通道ADC采集时给ADC采集引脚接上气体传感器后其数值变化浮动很大的情况,并且气体传感器在检测到对应气体时依旧我行我素地在那波动,而不是检测到气体后数值升高
07-14
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值