函数申明和变量申明的微妙区别

本文通过两个示例探讨了JavaScript中函数声明与变量声明之间的冲突问题。第一个示例展示了函数声明无法覆盖变量声明;而在第二个示例中,通过eval使用函数声明成功覆盖了变量声明,揭示了eval在作用域解析中的特殊行为。
<script>
var test1 = 1;
function test1(){};//函数申明不能覆盖变量申明?
alert(test1);
</script>
<script>
var test2 = 1;
eval('function test2(){};');
alert(test2);//函数申明怎么又覆盖了变量申明?
</script>
#include <stdio.h> #include "sys.h" #include "bsp_oled_iic.h" #include "oledFont.h" /********************************************************************************/ /* OLED显示模块基础配置宏定义 */ #define OLED_MODE 0 // OLED接口模式(0=I2C,1=SPI,需与硬件匹配) #define SIZE 8 // 基础字体大小(8x8像素) #define XLevelL 0x00 // 显存列地址低位起始值 #define XLevelH 0x10 // 显存列地址高位起始值(组合使用设置显存窗口) #define Max_Column 128 // 屏幕最大列数(像素宽度) #define Max_Row 64 // 屏幕最大行数(像素高度) #define Brightness 0xFF // 初始亮度值(0x00-0xFF,FF为最亮) #define X_WIDTH 128 // 物理屏幕宽度(像素) #define Y_WIDTH 64 // 物理屏幕高度(像素) #define OLED_CMD 0 // 操作类型:写命令(控制寄存器/参数) #define OLED_DATA 1 // 操作类型:写数据(显存内容) /********************************************************************************/ /* 基础硬件控制函数 */ void Delay_50ms(unsigned int Del_50ms); // 阻塞延时函数(单位:50ms周期数) void Delay_1ms(unsigned int Del_1ms); // 阻塞延时函数(单位:1ms周期数) /* I2C协议层操作 */ void IIC_Start(void); // 生成I2C起始条件(SCL高时SDA下降沿) void IIC_Stop(void); // 生成I2C停止条件(SCL高时SDA上升沿) void Write_IIC_Command(unsigned char IIC_Command); // 发送单字节命令(自动设置DC线为CMD模式) void Write_IIC_Data(unsigned char IIC_Data); // 发送单字节数据(自动设置DC线为DATA模式) void Write_IIC_Byte(unsigned char IIC_Byte); // 原始I2C字节发送(需手动控制DC线状态) void IIC_Wait_Ack(void); // 等待从设备应答(检测SDA线低电平) /* OLED显示控制层 */ void OLED_WR_Byte(unsigned dat,unsigned cmd); // 通用字节写入(cmd=0写命令,cmd=1写数据) void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char Char_Size); // 指定坐标显示字符 void OLED_Set_Pos(unsigned char x, unsigned char y); // 设置显存写入起始坐标(x:0-127, y:0-7对应8页) void fill_picture(unsigned char fill_Data); // 全屏填充统一数据(用于清屏或测试) /* 函数申明 -----------------------------------------------*/ extern void delay_us(unsigned int nus); /* 变量定义 ----------------------------------------------*/ /*----------------------------------------------------------- * 函数名:I2C_delay * 功能:I2C总线时序控制短延时 * 说明: * - 通过空循环实现微妙级延时 * - 经实测i=5为最低有效值(24MHz主频下约0.5us) * - 延时精度受编译器优化主频影响,移植需重新校准 *-----------------------------------------------------------*/ static void I2C_delay(void) { unsigned char i=5; // 这里可以优化速度,经测试最低到5还能写入 while(i) { i--; } } /*----------------------------------------------------------- * 函数名:delay5ms * 功能:产生约5ms延时(精度较低) * 说明: * - 空循环实现毫秒级延时 * - 实际延时时间与主频强相关(示例值基于特定时钟配置) * - 建议改用定时器实现精确延时 *-----------------------------------------------------------*/ static void delay5ms(void) { int i=50; // 循环次数经验值(24MHz下约5ms) while(i) { i--; } } /* * 函数名:I2C_Start * 功能:生成I2C起始条件并检测总线状态 * 返回值: * TRUE - 起始条件生成成功 * FALSE - 总线忙(SDA被拉低)或总线错误(无法拉低SDA) * 时序说明: * 1. 初始状态:SCLSDA均为高电平(总线空闲) * 2. 检测总线是否被占用(SDA=0表示总线忙) * 3. 产生起始条件(SDA在SCL高电平期间产生下降沿) * 4. 验证起始条件有效性(成功拉低SDA) */ static unsigned char I2C_Start(void) { SDA_H; // 释放SDA线(拉高) SCL_H; // 释放SCL线(拉高) I2C_delay(); // 等待电平稳定 /* 总线状态检测 */ if(!SDA_read) // 检测SDA线是否为低电平 return FALSE; // 总线被其他设备占用(返回错误) /* 生成起始条件 */ SDA_L; // 在SCL高电平期间拉低SDA(起始信号) I2C_delay(); /* 验证起始信号 */ if(SDA_read) // 检测SDA线是否成功拉低 return FALSE; // SDA无法被拉低(总线异常) SDA_L; // 保持SDA低电平(配合后续SCL操作) I2C_delay(); return TRUE; // 起始条件生成成功 } /* * 函数名:I2C_Stop * 功能:生成I2C停止条件并释放总线 * 时序说明: * 1. 确保SCL先置低(避免在SCL高时直接改变SDA形成起始条件) * 2. 将SDA置低建立稳定状态 * 3. 先拉高SCL再拉高SDA,在SCL高电平期间SDA上升沿形成停止条件 */ static void I2C_Stop(void) { SCL_L; // 先将SCL置低,为电平切换准备安全环境 I2C_delay(); // 等待电平稳定 SDA_L; // SDA置低建立明确电平状态 I2C_delay(); SCL_H; // 拉高SCL(此时SCLSDA均为高) I2C_delay(); SDA_H; // 在SCL高电平期间释放SDA(产生停止条件的上升沿) I2C_delay(); // 保证停止条件持续时间(>4us) } /* * 函数名:I2C_Ack * 功能:生成I2C应答信号(ACK) * 时序说明: * 1. SCL置低电平(数据线允许变化) * 2. SDA置低电平(ACK信号核心动作) * 3. SCL置高电平(主机检测ACK信号) * 4. SCL恢复低电平(为后续传输准备) * 协议规范: * - 应答信号发生在第9个时钟周期(每个字节传输后的ACK周期) * - SDA低电平表示成功接收(ACK),高电平表示无应答(NACK) */ static void I2C_Ack(void) { SCL_L; /* 时钟线置低,允许数据线变化 */ I2C_delay(); /* 等待电平稳定(满足tLOW时序) */ SDA_L; /* 数据线置低(生成ACK信号) */ I2C_delay(); /* 保持数据稳定(满足tSU:DAT时序) */ SCL_H; /* 时钟线置高(主机检测ACK信号) */ I2C_delay(); /* 维持高电平(满足tHIGH时序) */ SCL_L; /* 时钟线恢复低电平(结束ACK周期) */ I2C_delay(); /* 为下一数据传输周期准备 */ } /* * 函数名:I2C_NoAck * 功能:生成I2C非应答信号(NACK) * 协议规范: * 1. 在I2C协议中,主机在第9个时钟周期保持SDA高电平表示NACK * 2. NACK用于以下场景: * - 主机接收完最后一个字节数据 * - 从机无法响应时强制终止传输 * 时序说明: * 1. SCL先置低电平(数据线变化安全期) * 2. SDA置高电平建立NACK状态 * 3. SCL置高电平使从机锁存NACK信号 * 4. SCL恢复低电平完成信号周期 */ static void I2C_NoAck(void) { SCL_L; // 时钟线置低,允许数据线变化 I2C_delay(); // 等待电平稳定(tLOW时钟低周期时间) SDA_H; // 数据线置高(NACK信号核心动作) I2C_delay(); // 保持数据稳定(tSU:DAT数据建立时间) SCL_H; // 时钟线置高(从机在此上升沿采样NACK) I2C_delay(); // 维持高电平(tHIGH时钟高周期时间) SCL_L; // 时钟线恢复低电平(结束NACK周期) I2C_delay(); // 总线空闲准备(tBUF总线空闲时间) } /* 函数功能: 等待I2C从设备返回ACK应答信号 * 返回值 : 返回TRUE(1)表示收到ACK,返回FALSE(0)表示无ACK * 执行流程: * 1. 拉低时钟线(SCL_L)启动ACK检测流程 * 2. 释放数据线(SDA_H)使从设备可以控制SDA * 3. 拉高时钟线(SCL_H)生成ACK检测时钟脉冲 * 4. 在时钟高电平期间检测SDA状态: * - 高电平: 从设备无应答(NACK) * - 低电平: 从设备有应答(ACK) * 5. 最终保持时钟线低电平(SCL_L)结束ACK检测 */ static unsigned char I2C_WaitAck(void) { SCL_L; // 拉低时钟线开始ACK检测 I2C_delay(); // 保持时序 SDA_H; // 释放数据线(切换为输入模式) I2C_delay(); // 等待总线稳定 SCL_H; // 时钟线上升沿(从设备在此阶段拉低SDA) I2C_delay(); // 保持时钟高电平时间 if(SDA_read) // 检测SDA电平状态 { // 高电平 -> 无ACK SCL_L; // 结束ACK检测前拉低时钟 I2C_delay(); return FALSE; // 返回无应答状态 } SCL_L; // 收到ACK后保持时钟低电平 I2C_delay(); return TRUE; // 返回成功收到ACK } /* 函数功能: 通过I2C总线发送单字节数据(MSB优先) * 参数 : SendByte - 要发送的字节数据 * 执行流程: * 1. 循环8次依次发送每个bit(从最高位到最低位) * 2. 每个bit发送步骤: * a. 拉低SCL进入数据准备阶段 * b. 根据当前bit值设置SDA电平(高电平=1,低电平=0) * c. 左移数据准备下一个bit * d. 拉高SCL生成时钟上升沿(数据采样点) * e. 保持时钟高电平满足时序要求 * 3. 最终保持SCL低电平结束传输 */ static void I2C_SendByte(unsigned char SendByte) { unsigned char i=8; while(i--) // 循环发送8个bit { SCL_L; // 拉低时钟开始数据准备 I2C_delay(); // 保持低电平时间 /* 设置数据线电平 */ if(SendByte&0x80) // 检测最高位(0x80=10000000b) SDA_H; // 发送逻辑1 else SDA_L; // 发送逻辑0 SendByte<<=1; // 左移准备下一个bit(MSB优先) I2C_delay(); // 保证数据建立时间 SCL_H; // 时钟上升沿(从机在此阶段采样) I2C_delay(); // 保持高电平时间 } SCL_L; // 最终保持时钟低电平(总线空闲) } /* 函数功能: 通过I2C总线接收单字节数据(MSB优先) * 返回值 : 接收到的完整字节数据 * 执行流程: * 1. 初始化数据线为高电平(SDA_H)进入输入模式 * 2. 循环8次依次接收每个bit(从最高位到最低位) * 3. 每个bit接收步骤: * a. 左移接收字节准备存储新bit * b. 拉低SCL_L开始数据采样周期 * c. 拉高SCL_H生成时钟上升沿(从机在此阶段输出数据) * d. 在SCL高电平期间读取SDA状态 * e. 根据SDA电平设置接收字节最低位 * 4. 最终保持SCL_L低电平结束传输 */ static unsigned char I2C_RadeByte(void) { unsigned char i=8; unsigned char ReceiveByte=0; SDA_H; // 释放数据线(切换为输入模式) while(i--) // 循环接收8个bit { ReceiveByte<<=1; // 左移腾出最低位(MSB First) SCL_L; // 拉低时钟开始采样周期 I2C_delay(); // 保持低电平时间 SCL_H; // 时钟上升沿(从机输出数据稳定) I2C_delay(); // 保持高电平时间 if(SDA_read) // 检测SDA电平状态 { ReceiveByte|=0x01; // 最低位置1(小端存储) } } SCL_L; // 最终拉低时钟结束传输 return ReceiveByte; // 返回完整接收字节 } /* 函数功能: 向I2C从设备执行单字节数据写入操作 * 参数 : * SlaveAddress - 从设备地址(7位地址,不含读写位) * REG_Address - 目标寄存器地址 * REG_data - 待写入数据 * 返回值 : TRUE(1)操作成功,FALSE(0)操作失败 * 执行流程: * 1. 发起I2C起始信号 * 2. 发送从设备地址+写标志位(地址自动左移1位,最低位置0) * 3. 发送目标寄存器地址 * 4. 发送待写入数据 * 5. 每次发送后检测从设备应答 * 6. 发送停止信号结束通信 * 7. 延时5ms确保写入完成 */ unsigned char Single_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char REG_data) { if(!I2C_Start()) // 发起起始信号 { return FALSE; // 总线占用检测失败 } // 发送设备地址(自动添加写标志位) I2C_SendByte(SlaveAddress); if(!I2C_WaitAck()){ // 检测地址ACK I2C_Stop(); return FALSE; // 地址无应答 } I2C_SendByte(REG_Address); // 发送寄存器地址 I2C_WaitAck(); // 未处理ACK失败(需改进) I2C_SendByte(REG_data); // 发送写入数据 I2C_WaitAck(); // 未处理ACK失败(需改进) I2C_Stop(); // 终止通信 delay5ms(); // 等待数据持久化(EEPROM等需要) return TRUE; // 返回操作成功状态 } /* 函数功能: 从I2C从设备执行单字节数据读取操作 * 参数 : * SlaveAddress - 从设备地址(7位地址,不含读写位) * REG_Address - 要读取的目标寄存器地址 * 返回值 : * 成功时返回读取的字节数据(0x00-0xFF) * 失败时返回FALSE(0x00)(与数据0冲突,需业务层判断) * 执行流程: * 1. 发起起始信号 -> 发送设备地址(写模式) * 2. 发送寄存器地址 -> 重复起始信号 * 3. 发送设备地址(读模式) -> 接收数据 * 4. 发送NACK终止传输 -> 发送停止信号 */ unsigned char Single_Read(unsigned char SlaveAddress, unsigned char REG_Address) { unsigned char REG_data; // 第一阶段:写寄存器地址 if(!I2C_Start()) return FALSE; // 启动总线失败 I2C_SendByte(SlaveAddress); // 发送设备地址+写标志 if(!I2C_WaitAck()){ // 检测地址ACK I2C_Stop(); return FALSE; // 从机无应答 } I2C_SendByte(REG_Address); // 发送目标寄存器地址 I2C_WaitAck(); // 未处理ACK失败(建议改进) // 第二阶段:读取数据 I2C_Start(); // 重复起始信号 I2C_SendByte(SlaveAddress + 1); // 设备地址+读标志(LSB=1) I2C_WaitAck(); REG_data = I2C_RadeByte(); // 读取数据字节 I2C_NoAck(); // 发送NACK终止读取 I2C_Stop(); // 释放总线 return REG_data; // 返回读取结果 } /* 函数功能: 向I2C从设备执行多字节连续写入操作 * 参数 : * SlaveAddress - 从设备地址(7位地址,不含读写位) * REG_Address - 起始寄存器地址 * REG_data - 待写入数据缓冲区指针 * num - 要写入的字节数 * 返回值 : TRUE(1)操作成功,FALSE(0)操作失败 * 执行流程: * 1. 发起起始信号 -> 发送设备地址(写模式) * 2. 发送寄存器起始地址 * 3. 循环发送数据缓冲区内容 * 4. 每个字节发送后检测ACK * 5. 发送停止信号并延时5ms */ unsigned char IIC_Mult_Write(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char *REG_data, unsigned char num) { unsigned char i = 0; // 启动传输序列 if(!I2C_Start()) return FALSE; // 总线启动检测 I2C_SendByte(SlaveAddress); // 发送设备地址+写标志 if(!I2C_WaitAck()){ // 检测地址ACK I2C_Stop(); return FALSE; } I2C_SendByte(REG_Address); // 发送寄存器起始地址 I2C_WaitAck(); // 未处理ACK失败(需改进) // 循环发送数据 do{ I2C_SendByte(REG_data[i]); // 发送数据字节 I2C_WaitAck(); // 未处理ACK失败(数据可能丢失) i++; num--; } while (num); // 直到发送完指定字节数 I2C_Stop(); // 终止通信 delay5ms(); // 等待存储完成(EEPROM等需要) return TRUE; // 返回操作状态 } /* 函数功能: 从I2C从设备连续读取多个字节数据 * 参数 : * SlaveAddress - 7位从设备地址(不含读写位) * REG_Address - 目标起始寄存器地址 * REG_data - 数据接收缓冲区指针 * num - 要读取的字节数(≥1) * 返回值 : TRUE(1)操作成功,FALSE(0)操作失败 * 执行流程: * 1. 写相位阶段: * a. 发送起始信号 * b. 发送设备地址+写标志 * c. 发送目标寄存器地址 * 2. 读相位阶段: * a. 重复起始信号 * b. 发送设备地址+读标志 * c. 循环接收数据: * - 前N-1个字节发送ACK继续读取 * - 最后1个字节发送NACK终止读取 * 3. 发送停止信号释放总线 */ unsigned char IIC_Mult_Read(unsigned char SlaveAddress, unsigned char REG_Address, unsigned char *REG_data, unsigned char num) { unsigned char i = 0; // 第一阶段:写寄存器地址 if(!I2C_Start()) return FALSE; // 起始信号失败 I2C_SendByte(SlaveAddress); // 设备地址+写标志(LSB=0) if(!I2C_WaitAck()){ // 地址ACK检测 I2C_Stop(); return FALSE; } I2C_SendByte(REG_Address); // 发送寄存器起始地址 I2C_WaitAck(); // 未处理ACK失败(建议改进) // 第二阶段:读取数据流 I2C_Start(); // 重复起始信号 I2C_SendByte(SlaveAddress + 1); // 设备地址+读标志(LSB=1) I2C_WaitAck(); // 未处理ACK失败(建议改进) // 循环接收数据字节 num--; // 调整计数器(N-1个ACK) while (num--) { REG_data[i] = I2C_RadeByte(); // 读取字节存入缓冲区 i++; I2C_Ack(); // 发送ACK继续读取 } REG_data[i] = I2C_RadeByte(); // 读取最后一个字节 I2C_NoAck(); // 发送NACK终止读取 I2C_Stop(); // 释放总线 return TRUE; // 返回操作成功 } /* 1ms延时函数(近似值,具体时长需根据主频校准) */ void Delay_1ms(unsigned int Del_1ms) { unsigned char j; while(Del_1ms--) // 外层循环控制总延时毫秒数 { for(j=0;j<123;j++); // 内层循环实现约1ms延时(11.0592MHz典型值) } } /* OLED I2C操作状态标志 * 0:失败 1:成功 (由Single_Write返回值更新) */ unsigned char OLED_IIC_stu = 0; /* OLED单字节写入函数 * dat : 要写入的数据/命令 * cmd : 数据类型选择 * 1=数据模式(0X40) * 0=命令模式(0X00) */ void OLED_WR_Byte(unsigned dat,unsigned cmd) { if(cmd) // 数据写入模式 { OLED_IIC_stu = Single_Write(0x78,0X40,dat); // 0x78为OLED器件地址 } else // 命令写入模式 { OLED_IIC_stu = Single_Write(0x78,0X00,dat); // 0x00为命令寄存器地址 } } /* 全屏填充函数 * fill_Data : 填充模式(0x00全灭/0xFF全亮/0xAA棋盘格等) */ void fill_picture(unsigned char fill_Data) { unsigned char m,n; for(m=0;m<8;m++) // 遍历8个页(OLED每页对应8行) { OLED_WR_Byte(0xb0+m,0); // 设置页地址:0xB0~0xB7 OLED_WR_Byte(0x00,0); // 列地址低4位:0x00 OLED_WR_Byte(0x10,0); // 列地址高4位:0x10(组合后从0x10到0x17) for(n=0;n<128;n++) // 遍历128列 { OLED_WR_Byte(fill_Data,1); // 持续写入填充数据 } } } /* 坐标定位函数 * x : 列地址(0-127) * y : 页地址(0-7) * 注意:OLED纵向以页为单位,每页8个像素行 */ void OLED_Set_Pos(unsigned char x, unsigned char y) { OLED_WR_Byte(0xb0+y,OLED_CMD); // 设置页地址(0xB0+y) OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); // 列地址高4位(0x10|高半字节) OLED_WR_Byte((x&0x0f),OLED_CMD); // 列地址低4位(0x00|低半字节) } ///// //开启OLED显示 void oled_Display_On(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON } //关闭OLED显示 void oled_Display_Off(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF } //清屏函数,清完屏,整个屏幕是黑色的!没点亮一样!!! void oled_Clear(void) { unsigned char i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); } //更新显示 } void oled_On(void) { unsigned char i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA); } //更新显示 } /* 函数功能: 在OLED指定位置显示单个字符 * 参数说明: * x,y : 显示起始坐标(x:0-127列, y:0-7页) * chr : 要显示的ASCII字符(实际支持范围视字库而定) * Char_Size : 字体大小选择(16=16x8点阵, 其他值=6x8点阵) * 实现细节: * - 字符显示基于预存字模数据(F8X16/F6x8数组) * - 16px字体分两次写入,上半页下半页各8行数据 * - 自动换行处理: x超界时复位到0列并下移2页(16px字体适用) * 注意事项: * - 字库数组需包含从空格字符(ASCII 32)开始的字形数据 * - 页地址y参数实际对应OLED的8像素行区块 */ void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char Char_Size) { unsigned char c=0,i=0; c = chr - ' '; // 计算字库偏移量(空格字符为字库起点) /* 坐标超界处理(16px字体自动换行) */ if(x > Max_Column-1){ x = 0; y = y + 2; // 16px字体占2页高度,换行时增加2页 } if(Char_Size ==16) // 16x8点阵字体处理 { /* 写入上半页数据(前8行) */ OLED_Set_Pos(x,y); for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i], OLED_DATA); // 索引计算: 每个字符16字节 /* 写入下半页数据(后8行) */ OLED_Set_Pos(x,y+1); for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i+8], OLED_DATA); } else // 6x8点阵字体处理 { OLED_Set_Pos(x,y); for(i=0;i<6;i++) OLED_WR_Byte(F6x8[c][i], OLED_DATA); // 每个字符6字节 } } /* 函数功能: 简易整数幂计算(m的n次方) * 参数说明: * m : 底数(建议范围0-65535) * n : 指数(建议范围0-16, 防止结果溢出) * 返回值 : m^n计算结果(无符号整型) * 注意事项: * - 仅适用于非负整数运算 * - 无溢出保护,需确保结果在unsigned int范围内 * - 典型应用场景: OLED显示数值时的位权计算 */ unsigned int oled_pow(unsigned char m,unsigned char n) { unsigned int result=1; while(n--) result *= m; // 通过累乘实现幂运算 return result; } /* 函数功能: 多位数字显示(支持自动去前导零) * 参数说明: * x,y : 起始坐标(x:列,y:页) * num : 待显示数值(支持0~4294967295) * len : 显示位数(固定位数显示) * size2 : 字体大小(16/8 对应不同字库) * 实现特性: * - 自动跳过前导零显示(保留最后一位零) * - 数字间距自动计算(size2/2) * - 支持超过显示位数时截断高位 */ void oled_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char size2) { unsigned char t,temp; unsigned char enshow=0; // 前导零标志(0:未遇到有效数字) for(t=0;t<len;t++) { temp=(num/oled_pow(10,len-t-1))%10; // 提取指定位的数字 /* 前导零处理逻辑 */ if(enshow==0&&t<(len-1)) // 允许最后一位显示零 { if(temp==0) { OLED_ShowChar(x+(size2/2)*t,y,' ',size2); // 用空格替代前导零 continue; }else enshow=1; // 遇到非零数字后开始显示 } OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); // 数字转ASCII码显示 } } /* 函数功能: 字符串显示(支持自动换行) * 参数说明: * chr : 字符串指针(需以'\0'结尾) * Char_Size : 字体大小(16/8) * 显示特性: * - 字符间距固定8像素(适用于8px宽字体) * - x>120时换行到下一行首列 * - 换行时y增加2页(适配16px高度字体) */ void oled_ShowString(unsigned char x,unsigned char y,unsigned char *chr,unsigned char Char_Size) { unsigned char j=0; while (chr[j]!='\0') { OLED_ShowChar(x,y,chr[j],Char_Size); x+=8; // 步进8列(标准ASCII字符宽度) /* 边界换行处理 */ if(x>120){x=0;y+=2;} // 预留8列余量防止溢出 j++; } } /* 函数功能: 16x16点阵汉字显示(双页模式) * 参数说明: * x : 起始列坐标(0-127) * y : 起始页坐标(0-6) 每个汉字占2页高度 * no: 汉字在字库中的索引号(0~n) * 字库要求: * Hzk[][16]二维数组,每个汉字占32字节: * - [2*no] 为汉字上半部分数据(前16字节) * - [2*no+1] 为汉字下半部分数据(后16字节) * 执行流程: * 1. 定位到指定页的起始列 * 2. 写入前16字节到上半页(y) * 3. 定位到下一页(y+1) * 4. 写入后16字节到下半页 * */ void oled_ShowCHinese(unsigned char x,unsigned char y,unsigned char no) { unsigned char t,adder=0; // adder为无效变量(历史遗留) /* 写入汉字上半部分(8像素行) */ OLED_Set_Pos(x,y); // 设置起始位置(列x,页y) for(t=0;t<16;t++) // 遍历16列数据 { OLED_WR_Byte(Hzk[2*no][t],OLED_DATA); // 发送字库上半部数据 adder+=1; // 无效操作(可删除) } /* 写入汉字下半部分(8像素行) */ OLED_Set_Pos(x,y+1); // 跳转到下一页(y+1) for(t=0;t<16;t++) // 遍历16列数据 { OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA); // 发送字库下半部数据 adder+=1; // 无效操作(可删除) } } /* 函数功能: 显示十进制整型数值(带简单格式化) * 参数说明: * x,y : 显示起始坐标 * Dnum : 要显示的整型数值(-999~9999) * size1 : 字体大小(8/16) * 实现特性: * - 使用%3d格式保证至少显示3位数字(不足补空格) * - Data[5]=0强制截断为5字符(含结束符) * */ void OLED_ShowDNum(unsigned char x,unsigned char y,int Dnum,unsigned char size1) { uint8_t Data[20]= " "; // 格式化缓冲区(实际有效长度被限制为4字符+结束符) sprintf((char*)Data,"%3d",Dnum); // 格式化为至少3位十进制数 Data[5] = 0; // 强制终止字符串(防止长数值溢出) oled_ShowString(x,y,Data,size1); } /* 函数功能: 显示浮点数值(保留1位小数) * 参数说明: * Fnum : 要显示的浮点数值(-999.9~9999.9) * 实现特性: * - 使用%.1f格式保留1位小数 * - Data[5]=0强制截断为5字符(含结束符) * */ void OLED_ShowFNum(unsigned char x,unsigned char y,float Fnum,unsigned char size1) { uint8_t Data[20]= " "; // 格式化缓冲区(实际有效长度被限制为5字符+结束符) sprintf((char*)Data,"%.1f",Fnum); // 保留1位小数格式化 Data[5] = 0; // 强制终止字符串(示例"123.4"占5字节) oled_ShowString(x,y,Data,size1); } /* 函数功能: OLED位图绘制(存在潜在坐标计算问题) * 参数说明: * x0,y0 : 起始列坐标(0-127)起始页坐标(0-7) * x1,y1 : 结束列坐标(应大于x0)结束像素行坐标(0-63) * BMP[] : 位图数据数组(按纵向分页存储,每页包含x1-x0个列数据) * 实现流程: * 1. 计算结束像素行对应的总页数:y = y1/8 + 余数处理 * 2. 纵向逐页遍历(y0到计算得到的y值) * 3. 每页内横向逐列写入数据(x0到x1) * 4. 数据按列顺序连续存储,每列1字节(8像素行) * */ void oled_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[]) { unsigned int j=0; // 数据缓冲区索引 unsigned char x,y; // 列/页循环变量 /* 页数计算 */ if(y1%8==0) y=y1/8; // 完美对齐时直接除 else y=y1/8+1; // 未对齐时增加1页 /* 纵向页遍历 */ for(y=y0;y<y1;y++) // y0作为起始页,y1作为结束页 { OLED_Set_Pos(x0,y); // 设置当前页起始列 /* 横向列遍历 */ for(x=x0;x<x1;x++) { OLED_WR_Byte(BMP[j++],OLED_DATA); // 写入列数据 } } } //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // /* 函数功能: SSD1306 OLED显示屏初始化 * 执行流程: * 1. GPIO初始化: 配置PA11(SDA),PA12(SCL)为推挽输出 * 2. 发送初始化命令序列配置显示参数 * 3. 清屏并开启显示 * */ void oled_Init(void) { /* GPIO初始化阶段 */ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟 /* SCL(PA12)配置 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 高速模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // SCL引脚 GPIO_Init(GPIOA, &GPIO_InitStructure); /* SDA(PA11)配置 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // SDA引脚 GPIO_Init(GPIOA, &GPIO_InitStructure); /* 总线初始状态置高 */ GPIO_SetBits(GPIOA, GPIO_Pin_12); // SCL高电平 GPIO_SetBits(GPIOA, GPIO_Pin_11); // SDA高电平 /* 设备初始化阶段 */ while (!Single_Write(0x78,0X00,0xae)); // 持续尝试关闭显示直到成功 Delay_1ms(400); // 关键延时确保硬件稳定 /* SSD1306配置命令序列 */ OLED_WR_Byte(0xAE,OLED_CMD); // 关闭显示 OLED_WR_Byte(0x00,OLED_CMD); // 设置列地址低4位起始为0 OLED_WR_Byte(0x10,OLED_CMD); // 设置列地址高4位起始为0 OLED_WR_Byte(0x40,OLED_CMD); // 设置显示起始行地址(行0) OLED_WR_Byte(0xB0,OLED_CMD); // 设置页地址起始页(页0) OLED_WR_Byte(0x81,OLED_CMD); // 对比度控制模式 OLED_WR_Byte(0xFF,OLED_CMD); // 对比度最大值255 OLED_WR_Byte(0xA1,OLED_CMD); // 段重映射(水平翻转) OLED_WR_Byte(0xA6,OLED_CMD); // 正常显示模式(非反色) OLED_WR_Byte(0xA8,OLED_CMD); // 设置复用比率 OLED_WR_Byte(0x3F,OLED_CMD); // 1/64占空比(适用于128x64屏) OLED_WR_Byte(0xC8,OLED_CMD); // COM输出扫描方向(逆向) OLED_WR_Byte(0xD3,OLED_CMD); // 显示偏移设置 OLED_WR_Byte(0x00,OLED_CMD); // 无偏移 OLED_WR_Byte(0xD5,OLED_CMD); // 显示时钟分频 OLED_WR_Byte(0x80,OLED_CMD); // 建议默认值(分频比1) OLED_WR_Byte(0xD8,OLED_CMD); // 区域颜色模式 OLED_WR_Byte(0x05,OLED_CMD); // 禁用区域颜色模式 OLED_WR_Byte(0xD9,OLED_CMD); // 预充电周期设置 OLED_WR_Byte(0xF1,OLED_CMD); // Phase1=15 DCLK, Phase2=1 DCLK OLED_WR_Byte(0xDA,OLED_CMD); // COM引脚硬件配置 OLED_WR_Byte(0x12,OLED_CMD); // 顺序COM引脚,禁用左右COM偏移 OLED_WR_Byte(0xDB,OLED_CMD); // VCOMH电压等级 OLED_WR_Byte(0x30,OLED_CMD); // 约0.83*VCC OLED_WR_Byte(0x8D,OLED_CMD); // 电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); // 使能电荷泵(必须开启) OLED_WR_Byte(0xAF,OLED_CMD); // 开启显示 oled_Clear(); // 清屏初始化显示缓存 } /* 函数功能: 显示颜色反相控制 * 参数说明: * i - 显示模式选择 * 0: 正常显示(黑底白字) * 1: 反色显示(白底黑字) * 实现原理: * 通过发送SSD1306的A6/A7命令切换全局颜色映射 * */ void oled_ColorTurn(uint8_t i) { if(i==0) { OLED_WR_Byte(0xA6,OLED_CMD); // 0xA6: 正常像素模式(GDDRAM数据1表示亮) } if(i==1) { OLED_WR_Byte(0xA7,OLED_CMD); // 0xA7: 反色像素模式(GDDRAM数据0表示亮) } } /* 函数功能: 屏幕显示方向旋转控制 * 参数说明: * i - 旋转模式选择 * 0: 正常方向显示 * 1: 180度旋转显示 * 实现原理: * 组合使用段重映射(A0/A1)COM扫描方向(C0/C8)命令实现硬件级旋转 * 硬件配置: * - 0xC8: COM输出逆向扫描(从COM63到COM0) * - 0xA1: 段重映射(列地址127映射到SEG0) * - 0xC0: COM输出正常扫描(从COM0到COM63) * - 0xA0: 段正常映射(列地址0映射到SEG0) * */ void oled_DisplayTurn(uint8_t i) { if(i==0) // 标准方向 { OLED_WR_Byte(0xC8,OLED_CMD); // COM逆向扫描(配合物理布局) OLED_WR_Byte(0xA1,OLED_CMD); // 列地址反向(SEG127->SEG0) } if(i==1) // 旋转180度 { OLED_WR_Byte(0xC0,OLED_CMD); // COM正向扫描 OLED_WR_Byte(0xA0,OLED_CMD); // 列地址正常映射 } }
11-15
内容概要:本文围绕EKF SLAM(扩展卡尔曼滤波同步定位与地图构建)的性能展开多项对比实验研究,重点分析在稀疏与稠密landmark环境下、预测与更新步骤同时进行与非同时进行的情况下的系统性能差异,并进一步探讨EKF SLAM在有色噪声干扰下的鲁棒性表现。实验考虑了不确定性因素的影响,旨在评估不同条件下算法的定位精度与地图构建质量,为实际应用中EKF SLAM的优化提供依据。文档还提及多智能体系统在遭受DoS攻击下的弹性控制研究,但核心内容聚焦于SLAM算法的性能测试与分析。; 适合人群:具备一定机器人学、状态估计或自动驾驶基础知识的科研人员及工程技术人员,尤其是从事SLAM算法研究或应用开发的硕士、博士研究生相关领域研发人员。; 使用场景及目标:①用于比较EKF SLAM在不同landmark密度下的性能表现;②分析预测与更新机制同步与否对滤波器稳定性与精度的影响;③评估系统在有色噪声等非理想观测条件下的适应能力,提升实际部署中的可靠性。; 阅读建议:建议结合MATLAB仿真代码进行实验复现,重点关注状态协方差传播、观测更新频率与噪声模型设置等关键环节,深入理解EKF SLAM在复杂环境下的行为特性。稀疏 landmark 与稠密 landmark 下 EKF SLAM 性能对比实验,预测更新同时进行与非同时进行对比 EKF SLAM 性能对比实验,EKF SLAM 在有色噪声下性能实验
内容概要:本文围绕“基于主从博弈的售电商多元零售套餐设计与多级市场购电策略”展开,结合Matlab代码实现,提出了一种适用于电力市场化环境下的售电商优化决策模型。该模型采用主从博弈(Stackelberg Game)理论构建售电商与用户之间的互动关系,售电商作为领导者制定电价套餐策略,用户作为跟随者响应电价并调整用电行为。同时,模型综合考虑售电商在多级电力市场(如日前市场、实时市场)中的【顶级EI复现】基于主从博弈的售电商多元零售套餐设计与多级市场购电策略(Matlab代码实现)购电组合优化,兼顾成本最小化与收益最大化,并引入不确定性因素(如负荷波动、可再生能源出力变化)进行鲁棒或随机优化处理。文中提供了完整的Matlab仿真代码,涵盖博弈建模、优化求解(可能结合YALMIP+CPLEX/Gurobi等工具)、结果可视化等环节,具有较强的可复现性工程应用价值。; 适合人群:具备一定电力系统基础知识、博弈论初步认知Matlab编程能力的研究生、科研人员及电力市场从业人员,尤其适合从事电力市场运营、需求响应、售电策略研究的相关人员。; 使用场景及目标:① 掌握主从博弈在电力市场中的建模方法;② 学习售电商如何设计差异化零售套餐以引导用户用电行为;③ 实现多级市场购电成本与风险的协同优化;④ 借助Matlab代码快速复现顶级EI期刊论文成果,支撑科研项目或实际系统开发。; 阅读建议:建议读者结合提供的网盘资源下载完整代码与案例数据,按照文档目录顺序逐步学习,重点关注博弈模型的数学表达与Matlab实现逻辑,同时尝试对目标函数或约束条件进行扩展改进,以深化理解并提升科研创新能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值