51单片机使用74HC595时编写移位代码遇到的问题

[复制链接]
11160|17
 楼主| JZ在努力 发表于 2025-7-28 10:40 | 显示全部楼层 |阅读模式
在编写74HC595相关代码时,关于串行发送,目的是依次点亮LED点阵某一行temp = dat<<i;
SER = temp>>7;

SER  = dat >>7;
dat <<=1;这两种写法是对的,演示结果与预期一致。
但SER  = (dat<<i)>>7;这种写法理应也是对的,但演示结果是先全部点亮然后一次灭一行
请求各位大佬解答
LcwSwust 发表于 2025-7-28 11:31 | 显示全部楼层
试一下以下两种写法看有没有能用的
SER  = (unsigned int)(dat<<i)>>7
SER  = (unsigned char)(dat<<i)>>7

评论

@JZ在努力 :这样,给dat一个固定的值,用你的两种写法,把SER的值通过串口助手或显示屏或调试看看具体有什么区别.  发表于 2025-7-28 16:21
尝试过了,都没有能用的,分开写就正确  发表于 2025-7-28 16:13
xch 发表于 2025-7-28 11:59 | 显示全部楼层
本帖最后由 xch 于 2025-7-28 12:00 编辑

temp, dat, i, SER 是什么数据类型?分开写与合起来写估计数据类型转换不一样影响结果

评论

xch
@JZ在努力 :temp,dat 类型改成16或者32位无符号的结果可能就一样了  发表于 2025-7-28 19:09
temp、dat和i都是无符号char类型的,SER是一位的IO口,  发表于 2025-7-28 16:11
dffzh 发表于 2025-7-28 17:14 | 显示全部楼层
本帖最后由 dffzh 于 2025-7-28 17:20 编辑

按照你发的三种方式,实测如下:
将SER = (dat<<i)>>7;改成坛友说的 SER = (unsigned char)(dat<<i)>>7;实测如下,貌似可以的呀,你的改了也不行?
另外有个建议,因为C语言在数据类型转换这块,其实是比较复杂的,对于类似这种多个运算符参与的逻辑运算,分开多条语句实现比全部用一条语句实现更好,代码可读性更强,也不容易出错。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
567 发表于 2025-7-28 20:43 | 显示全部楼层
整形提升
CelestialScribe 发表于 2025-7-29 08:33 | 显示全部楼层
居然真的还有人在用51?
zjsx8192 发表于 2025-7-29 09:06 | 显示全部楼层
估计sfr转换不过来,要用if来判断

szyuan 发表于 2025-8-6 14:03 | 显示全部楼层

51单片机使用74HC595时编写移位代码遇到的问题

?????
龙猫王子 发表于 2025-8-6 19:46 来自手机 | 显示全部楼层
好久没用,快忘记了
wangwu1976@ 发表于 2025-9-9 08:12 | 显示全部楼层

51单片机使用74HC595时编写移位代码遇到的问题

可以分成两步,然后跟踪一下
wangwu1976@ 发表于 2025-9-9 08:13 | 显示全部楼层

51单片机使用74HC595时编写移位代码遇到的问题

可以分成两步,然后跟踪一下
kangjian2111 发表于 2025-9-9 14:22 | 显示全部楼层
zhuls 发表于 2025-9-27 17:57 | 显示全部楼层

51单片机使用74HC595时编写移位代码遇到的问题

估计拆开写的话,就烧了MCU了。
sunjd 发表于 2025-10-27 16:05 | 显示全部楼层

51单片机使用74HC595时编写移位代码遇到的问题

这涉及到C语言中的整数提升(integer promotion)和符号扩展(sign extension)问题。
问题出在表达式求值过程中的数据类型变化
分步操作时

temp = dat << i;     // dat是unsigned char,移位后可能提升为int
SER = temp >> 7;     // 但此时temp已经存储在变量中,类型确定

单表式达操作时

SER = (dat << i) >> 7;
// 编译器处理过程:
// 1. dat << i  → 结果可能是int类型(整数提升)
// 2. 如果dat是有符号char,可能产生符号扩展
// 3. >> 7 对提升后的int进行操作,得到意外结果


方案1:使用无符号类型和强制转换

// 确保dat是无符号类型
unsigned char dat = 0xFF;
// 或者在移位时强制转换
SER = (unsigned char)(dat << i) >> 7;


方案2:使用掩码确保正确位操作
SER = (dat << i) & 0x80 ? 1 : 0;  // 检查最高位
方案3: 使用位操作函数

// 定义清晰的位操作函数
void shift_out_byte(uint8_t data) {
    for(int i = 7; i >= 0; i--) {
        SER = (data >> i) & 0x01;  // 明确取第i位
        // 产生时钟脉冲
        SRCLK = 1;
        SRCLK = 0;
    }
}


完整的示例

#include <stdint.h>
// 明确的类型定义
#define SER   P1_0
#define SRCLK P1_1
#define RCLK  P1_2
// 推荐写法 - 清晰明确
void shift_out_safe(uint8_t data) {
    for(int8_t i = 7; i >= 0; i--) {
        SER = (data >> i) & 0x01;  // 取第i位,结果非0即1
        SRCLK = 1;
        SRCLK = 0;
    }
}

// 如果要使用你的原始思路
void shift_out_original(uint8_t data) {
    uint8_t temp;
    for(int i = 0; i < 8; i++) {
        temp = data << i;      // 分步操作
        SER = temp >> 7;       // 避免整数提升问题
        SRCLK = 1;
        SRCLK = 0;
    }
}




避免在复杂表达式中直接移位,特别是涉及不同数据类型的混合运算
使用无符号类型进行位操作
分步操作比单表达式更安全
明确的位提取(& 0x01)比依赖移位结果更可靠


您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

3

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部