循环偏移求最终位置以及根据数字的每一位求数的大小

1. eg:

[0 1 2 3 4 5 6 7 8 9]
求以start=3为起点,向左移56位的结果(0左移一位为9)

  • 一共10个数字,移10位会回到起点。实际移位数x=56%10,即6
  • 6 >3,会转回右边。差4个就可以回到起点三,所以相当于从3右移4位。

2. 例题1

解答
在这里插入图片描述
建立在1<=d<=61
num-d+62即num+右移的位数
(num-d+62)%62,将超过0-61的部分转为正确的下标

3. 例题2

解答
在这里插入图片描述

  • 求数的大小
int[] a = {1,2,3,4,2,0};
int res = 0for(int val : a){
	rrs = res*10 + val;//注意数字是X进制的就乘X
}
return res;

4. 总结

  • 若移位k > n,实际移位为k%n
  • 求循环偏移的位置左移k位相当于右移n-k位
  • 将下标超过0-n-1的转换为正确的下标
    (start+k)%n
<think>我们以数字"123"为例,详细解析在51单片机IIC总线OLED屏幕上显示数字时,`OLED_ShowNum`函数和`oled_pow`函数的逐位分离过程。 ### 1. 函数调用 假设调用方式为:`OLED_ShowNum(0, 0, 123, 3, 16)` - 起点坐标:`(x=0, y=0)` - 显示数字:`num=123` - 数字位数:`len=3`(显示3位) - 字体大小:`sizey=16`(16×16点阵) ### 2. 逐位分离过程 函数内部处理流程如下: ```c void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 sizey) { u8 t, temp, m = 0; u8 enshow = 0; // 前导零标志(0表示不显示前导零) if (sizey == 8) m = 2; // 8号字体时额外间距调整(本例sizey=16,故m=0) // 循环处理每一位(共3次循环) for (t = 0; t < len; t++) { // 步骤1:计算当前位权值(10的幂) // len-t-1: 第一次循环为2(百位),第二次为1(十位),第三次为0(个位) u32 pow_val = oled_pow(10, len-t-1); // 调用幂函数 // 步骤2:提取当前位数字(0-9) temp = (num / pow_val) % 10; // 步骤3:处理前导零(除最后一位外,高位为零时显示空格) if (enshow == 0 && t < (len-1)) { // 未遇到非零且不是最后一位 if (temp == 0) { // 显示空格(跳过当前位) OLED_ShowChar(x + (sizey/2 + m)*t, y, ' ', sizey); continue; // 跳过后续显示 } else { enshow = 1; // 遇到非零,后续位全部显示 } } // 步骤4:显示当前位数字数字转ASCII:temp+'0') OLED_ShowChar(x + (sizey/2 + m)*t, y, temp+'0', sizey); } } ``` #### 具体分解(以"123"为例): - **第1次循环(t=0,处理百位)**: - `len-t-1 = 3-0-1 = 2` → 计算 `pow_val = oled_pow(10,2) = 100` - `temp = (123/100) % 10 = 1 % 10 = 1` - 前导零判断:`enshow=0`(初始)且 `t=0 < 2`(len-1=2),但 `temp=1 ≠0` → 设置 `enshow=1` - 显示:调用 `OLED_ShowChar(0 + (16/2+0)*0, 0, '1', 16)` → 在坐标(0,0)显示'1' - **第2次循环(t=1,处理十位)**: - `len-t-1 = 3-1-1 = 1` → `pow_val = oled_pow(10,1)=10` - `temp = (123/10) % 10 = 12 % 10 = 2` - 前导零判断:`enshow=1`(已置位)→ 跳过判断 - 显示:调用 `OLED_ShowChar(0 + 8*1, 0, '2', 16)` → 在坐标(8,0)显示'2' - **第3次循环(t=2,处理个位)**: - `len-t-1=3-2-1=0` → `pow_val=oled_pow(10,0)=1` - `temp=(123/1)%10=123%10=3` - 前导零判断:`t=2`等于`len-1=2`(最后一位)→ 即使为零也显示 - 显示:调用 `OLED_ShowChar(0+8*2,0,'3',16)` → 在坐标(16,0)显示'3' #### 显示效果: ``` 坐标: (0,0) (8,0) (16,0) 显示: 1 2 3 ``` ### 3. 关键函数`oled_pow`解析 幂函数计算 $10^n$ 的值: ```c u32 oled_pow(u8 m, u8 n) { u32 result = 1; while (n--) result *= m; // 循环n次,累乘m return result; } ``` - 示例:`oled_pow(10,2)`: - 初始化:`result=1` - 循环2次: - 第1次:`n=2` → `result=1*10=10`,`n`减为1 - 第2次:`n=1` → `result=10*10=100`,`n`减为0 - 返回100 ### 4. 底层字符显示`OLED_ShowChar` 对于每个数字字符(如'1'),其显示过程: 1. 计算字符在字库中的偏移量:`c = '1' - ' ' = 49 - 32 = 17` 2. 根据字体大小选择字模数据: - 16×16字体:查表`asc2_1608[17]`(实际为16×8,需分两半显示) 3. 分两次写入(每8行一页): - 先写上半部分8×8数据(页0) - 再写下半部分8×8数据(页1) > 注:具体字模数据存储在常量数组中,如引用[3]中描述的ASCII字库[^3]。 ### 5. IIC通信底层 最终通过IIC协议发送数据到OLED: 1. 写命令设置光标位置(`OLED_Set_Pos`) 2. 循环发送字模数据(`OLED_WR_Byte`) - 每次发送一个字节(8个像素点) 如引用[3]所示,IIC数据包格式为: - 起始信号 → 从机地址(0x78) → 控制字节(0x00/0x40)→ 数据字节 → 停止信号[^3]。 *** §§相关问题§§ 1. 如何修改代码以实现固定位数显示(如始终显示3位数字,不足补零)? 2. 若需要显示负数(如-123),应如何扩展当前函数? 3. 在低功耗应用中,如何优化数字显示以减少IIC通信次数? </think>### 数字"123"在 OLED_ShowNum 函数中的逐位分离过程解析 以下以调用 `OLED_ShowNum(0, 0, 123, 3, 16)` 为例(起点坐标(0,0),显示数字123,3位数,16×16字体),详细展示逐位分离过程: #### 1. **初始参数** ```c num = 123 len = 3 sizey = 16 → m = 0 (因sizey≠8) enshow = 0 (前导零标志) ``` #### 2. **循环处理(逐位分离)** **第一轮循环 (t=0)** ```c temp = (123 / oled_pow(10, 3-0-1)) % 10 = (123 / oled_pow(10, 2)) % 10 = (123 / 100) % 10 = (1) % 10 = 1 ``` - 前导零检测:`enshow=0` 且 `t(0)<len-1(2)` → 进入判断 - `temp=1≠0` → 设置 `enshow=1`(后续位不再检查前导零) - 显示:`OLED_ShowChar(0 + (16/2+0)*0, 0, '1', 16)` → 坐标(0,0)显示'1' **第二轮循环 (t=1)** ```c temp = (123 / oled_pow(10, 3-1-1)) % 10 = (123 / oled_pow(10, 1)) % 10 = (123 / 10) % 10 = (12) % 10 = 2 ``` - `enshow=1` → 跳过前导零检测 - 显示:`OLED_ShowChar(0 + (8)*1, 0, '2', 16)` → 坐标(8,0)显示'2'(字体宽度为8像素) **第三轮循环 (t=2)** ```c temp = (123 / oled_pow(10, 3-2-1)) % 10 = (123 / oled_pow(10, 0)) % 10 = (123 / 1) % 10 = 123 % 10 = 3 ``` - 显示:`OLED_ShowChar(0 + (8)*2, 0, '3', 16)` → 坐标(16,0)显示'3' #### 3. **坐标计算原理** - 字符间距公式:`x + (sizey/2 + m)*t` - `sizey/2 = 8`(16×16字体的宽度) - `m = 0` - 每字符偏移:`t=0 → 0`, `t=1 → 8`, `t=2 → 16` - 最终显示效果: ``` 坐标: (0,0) (8,0) (16,0) 显示: 1 2 3 ``` ### 关键函数细节说明 #### ▶ `oled_pow(10, n)` 计算过程 ```c // 计算 10^n(n=指数) u32 oled_pow(u8 m, u8 n) { u32 result = 1; while(n--) result *= m; // 循环乘法 return result; } ``` - **示例**:`oled_pow(10, 2)` - 初始化:`result=1` - 第1次循环:`n=2 → result=1*10=10` - 第2次循环:`n=1 → result=10*10=100` - 返回 `100` #### ▶ `OLED_ShowChar` 字符显示流程(以'1'为例) 1. **字模索引计算**: ```c c = '1' - ' ' = 49 - 32 = 17 // 字库偏移量 ``` 2. **字模数据获取**: - 使用16×16字体(`sizey=16`)→ 查表 `asc2_1608[17]` - 字模数据示例:`{0x00, 0x00, 0x3F, 0x20, 0x20, ...}`(16字节)[^3] 3. **IIC写入过程**: - 发送从机地址:`0x78`(OLED地址) - 发送控制字节:`0x00`(指令模式)→ 设置光标位置 - 发送数据字节:`0x40`(数据模式)→ 逐个写入字模字节 ```c // 简化的IIC序列(引用[3]) IIC_Start(); IIC_SendByte(0x78); // 从机地址 IIC_SendByte(0x00); // 指令模式 IIC_SendByte(0xB0 | y); // 设置页地址(y坐标) IIC_SendByte(0x40); // 数据模式 for(i=0; i<16; i++) IIC_SendByte(asc2_1608[17][i]); // 字模数据 IIC_Stop(); ``` ### 设计要点总结 1. **动态位分离**: 通过 `(num / 10^{len-t-1}) % 10` 数学运算实现高位到低位的精确提取[^1]。 2. **前导零处理**: `enshow` 标志确保高位零显示为空格(如 `012` 显示为" 12"),但最低位零始终显示。 3. **坐标自适应**: 公式 `(sizey/2 + m)*t` 自动计算字符间距,支持不同字体尺寸的无缝切换。 ***
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值