找回密码
 立即注册
查看: 81|回复: 7

stc8h2k12u硬件spi驱动ws2812问题求助

[复制链接]
已绑定手机

9

主题

9

回帖

1955

积分

金牌会员

积分
1955
发表于 前天 22:05 来自手机 | 显示全部楼层 |阅读模式
mosi引脚每发送一个字节都会有一个高电平,导致ws2812的数据帧被截断怎么办

我也设置了miso和mosi未推挽输出,并且置0:


#include <Ai8h.H>
#include <INTRINS.H>
#include "Hardware_spi.h"

#define WS2812_CODE_1                        0x7f        //0x7F/0xFE  33.1176MHz/*0x60        //ws2812的1码 11.0592MHz*/        
#define WS2812_CODE_0                        0x70        //0x70/0xE0  33.1176MHz/*0x40        //ws2812的0码 11.0592MHz*/
#define WS2812_PIN_LOW                P33        //MISO脚需使用spi前置0,防止空闲时候的高电平干扰

unsigned char xdata wsColorBuf[24]={0};

void Ws2812_WriteColor(unsigned char wsGreen,unsigned char wsRed,unsigned char wsBlue)
{
        unsigned char i;
        for(i=0;i<8;i++)
        {
                if(wsGreen & (0x80>>i))
                {
                        wsColorBuf=WS2812_CODE_1;
                }
                else
                {
                        wsColorBuf=WS2812_CODE_0;
                }
        }
        for(i=0;i<8;i++)
        {
                if(wsRed & (0x80>>i))
                {
                        wsColorBuf[8+i]=WS2812_CODE_1;
                }
                else
                {
                        wsColorBuf[8+i]=WS2812_CODE_0;
                }
        }
        for(i=0;i<8;i++)
        {
                if(wsBlue & (0x80>>i))
                {
                        wsColorBuf[16+i]=WS2812_CODE_1;
                }
                else
                {
                        wsColorBuf[16+i]=WS2812_CODE_0;
                }
        }
}

void Ws2812_Update(void)
{
        unsigned char i;
        P33=0;
        
        SPCTL |=0x40;
        for(i=0;i<24;i++)
        {
                SPI_SendByte(wsColorBuf);
        }
        SPCTL &=~0x40;
        P34=0;
        
}


同样的代码,我下载给ai8h8k64u就是正常的,这是芯片的问题吗



Image_1794319439670_687.png
截图202611102232338191.jpg
回复

使用道具 举报 送花

已绑定手机

52

主题

2903

回帖

3045

积分

荣誉版主

积分
3045
发表于 前天 23:33 | 显示全部楼层
点个幻彩灯 何必单恋WS2812可以看看APA102@2线制,标准SPI
https://www.stcaimcu.com/thread-22209-1-1.html
(出处: 国芯人工智能技术交流网站)
睁开眼睛做场梦~~~
回复

使用道具 举报 送花

50

主题

392

回帖

3096

积分

超级版主

积分
3096
发表于 昨天 08:44 | 显示全部楼层

手册已确认关键差异,问题根源找到了。

一、根本原因:STC8H2K12U 是“快速 SPI”,与 STC8H8K64U 不是同一个 SPI 模块

手册《同步串行外设接口SPI》开头的功能支持表显示:

产品线 SPI 快速SPI 接收超时中断 MISO和MOSI可切换
STC8H8K64U系列A版本 ●
STC8H8K64U系列B/C/D版本 ●
STC8H2K12U-A/B系列 ● ● ●

也就是说:

  • STC8H2K12U 只有“快速 SPI”,并且支持“MISO 和 MOSI 可切换”;
  • 老 STC8H8K64U 的 SPI 与它不是同一套。

这意味着 STC8H2K12U 的 SPCTL 里有一个 “MISO/MOSI 交换”控制位(STC8H8K64U 没有这个位)。您原先在 8K64U 上调试通过的那套“用 MISO 输出、MOSI 置 0”的 WS2812 写法,换到 2K12U 上因为多了这个交换位、并且 MOSI 侧行为不同,就是MOSI 上每字节冒出一段高电平的直接来源。

二、WS2812 用 SPI 的正确思路(您现在的做法本身是对的)

WS2812 的 0 码/1 码靠高低电平占空比区分。用 SPI 模拟的常规做法是:把 1 个 WS2812 bit 编成 3 个 SPI bit(例如 100 = 0 码、110 = 1 码,具体看主频),数据从 MOSI 或 MISO 之一 输出到 WS2812 的 DIN。

您现在的方案是“让 MISO(P3.3)输出波形,MOSI(P3.4)拉低不管”,在 8K64U 上能跑通。但在 2K12U 上:

  1. MOSI 在 SPI 使能时是硬件驱动的,您用 P34=0 拉低,但只要 SPI 一工作,MOSI 输出端就按移位寄存器驱动,串行帧之间的空闲期和每字节间隙会出现高电平,这段高电平如果和 P3.3 上真正的 WS2812 波形电气上连通(或 WS2812 的 DIN 实际接在 P3.4 上),就会把数据帧切断。
  2. 请先确认 WS2812 的 DIN 实际焊在哪根脚上。如果接的是 P3.4(MOSI),那您就一直在“用 MISO 发数据、让 MOSI 干扰”,彻底搞错了脚。

三、处理办法(按优先级)

办法 1(推荐):改用“MISO/MOSI 交换位”,让 MOSI 输出波形

STC8H2K12U 支持 MISO 和 MOSI 可切换,这是 8K64U 没有的能力,正好用来解决这个问题。请查您手上头文件里 SPCTL 是否有类似 SPI_MOSI_MISO_SWAP 的名字,然后再决定具体位。

说明:手册这一章的功能支持表明确写了 STC8H2K12U 支持“MISO 和 MOSI 可切换”,但该位在 SPCTL 中的确切位号,我在这段检索结果里没能拿到原文。请您把 STC8H 手册《同步串行外设接口SPI》中 SPCTL 寄存器的位定义表发给我,我按位号给您准确写。在没确认位号前,请勿随意置位 SPCTL 的 B6 或 B7,避免改错配置。

办法 2:不改交换位,改为“让 MOSI 引脚彻底脱离 SPI”

如果 WS2812 的 DIN 确实接在 P3.3(MISO),那思路是不让 MOSI 输出任何东西:

  • 把 MOSI(P3.4)配置为高阻输入或推挽输出并保持 0:P3M1 |= 0x10; P3M0 &= ~0x10;(P3.4 为高阻输入),注意这是 P3 口的 B4 位。
  • 更关键的是:SPI 一使能,MOSI 就被硬件接管。若要保持 MOSI 不动,只能在 SPI 传输完成、SPIF 置位后立即关掉 SPI 使能(清 SPEN),下次发送前再打开。但这样会破坏 WS2812 对时序连续性的要求,不推荐。

所以若 DIN 接 P3.3,正确做法是把 DIN 改焊到 MOSI(P3.4),然后用办法 1 或直接按 MOSI 常规输出波形。

办法 3:SPI_S 功能脚切换要选对

手册《同步串行外设接口SPI》的功能脚切换表:

SPI_S[1:0]SSMOSIMISOSCLK

00P1.2/P5.4P1.3P1.4P1.5
01P2.2P2.3P2.4P2.5
10P5.4P4.0P4.1P4.3
11P3.5P3.4P3.3P3.2

注意:只有 SPI_S = 11 时,MOSI 才是 P3.4、MISO 才是 P3.3。您的代码默认 P_SW2 里 SPI_S[1:0] 可能还是别的值(8K64U 与 2K12U 复用寄存器也可能有差异),如果 SPI_S 不是 11,您以为的 P3.3/P3.4 根本没接到 SPI 上。请先确认 P_SW2 中 SPI_S 两位(B3、B2)设置。

四、您代码中已发现的两个明确错误

错误 1:wsColorBuf 写入没写下标(编译能过但逻辑错)

for(i=0;i<8;i++)
{
    if(wsGreen & (0x80>>i))
    {
        wsColorBuf=WS2812_CODE_1;      // ← 缺下标 [i]
    }
    else
    {
        wsColorBuf=WS2812_CODE_0;      // ← 缺下标 [i]
    }
}

wsColorBuf 是数组名,wsColorBuf = 0x7f; 在 C 里是“给数组首地址赋值”,语法上应报错;若您实际能编译通过,说明您贴的代码是被改动过的片段。必须写成 wsColorBuf[i] = ...。第二、三段已写对,第一段漏了。

错误 2:WS2812_PIN_LOW 定义后从未使用

#define WS2812_PIN_LOW  P33

全文件没有一处 WS2812_PIN_LOW = 0;。您注释里写“MISO脚需使用spi前置0,防止空闲时候的高电平干扰”,但代码里没有执行。Ws2812_Update() 里只有 P33=0;,这个是对的要保留;但发送结束后 P34=0; 把 MOSI 拉低了,在 2K12U 上这会让 MOSI 在下次 SPI 使能瞬间产生一个跳变,这很可能就是您看到“每字节一个高电平”的另一个来源。

五、修改后的代码

// =====================================================================
// STC8H2K12U 硬件快速SPI 驱动 WS2812
//
// 【使用说明】
// 1. 本代码基于 STC8H2K12U 的“快速SPI”,用 SPI_S=11:MOSI=P3.4, MISO=P3.3, SCLK=P3.2
//    WS2812 的 DIN 请接到 P3.4(MOSI)
// 2. 主频按 FOSC 修改。WS2812 的 0码/1码 由 3 个 SPI bit 组成,SPI 速度需约 2.4MHz
//    (1 个 SPI bit ≈ 0.4us,3 个 ≈ 1.2us)
// 3. SPCTL 的 MISO/MOSI 交换位请按手册确认位号后再决定是否使用,
//    本代码不置该位,保持默认
// 4. SPI 发送完成后必须等待 SPIF 置位再发下一个字节,否则会丢帧
// 5. 请在 Keil C51 下编译,头文件按您实际文件名包含
// =====================================================================

#include "STC8H.h"                  // 按实际头文件名修改
#include 

#define FOSC            24000000UL  // 系统主频 24MHz

// WS2812 编码:每个 bit 用 3 个 SPI bit 表示
// 24MHz 主频下 SPI 时钟 = SYSclk/2 或 SYSclk/4,按实测微调
#define WS2812_CODE_1   0xFC        // 1码:1111 1100(3位一组,全1在前)
#define WS2812_CODE_0   0xC0        // 0码:1100 0000(3位一组,只有前面1)

// SPI 功能脚选择:11 -> MOSI=P3.4, MISO=P3.3, SCLK=P3.2
#define SPI_PIN_SEL     3

unsigned char xdata wsColorBuf[24];

// ---------------------- SPI 初始化 ----------------------
void SPI_Init(void)
{
    P_SW2 = (P_SW2 & 0xF3) | (SPI_PIN_SEL << 2);   // SPI_S[1:0] 在 B3、B2

    // P3.2(SCLK) / P3.4(MOSI) 推挽输出;P3.3(MISO) 不用于 WS2812 时可不管
    P3M1 &= ~0x14;   // 清 P3.2、P3.4 的 M1
    P3M0 |=  0x14;   // 置 P3.2、P3.4 的 M0 -> 推挽输出

    SPCTL = 0xD0;    // 使能SPI, 主机模式, MSB先发, 时钟空闲低, 上升沿采样
                     // 具体位请按手册 SPCTL 位定义核对后再用
}

// ---------------------- SPI 发送一个字节 ----------------------
void SPI_SendByte(unsigned char dat)
{
    SPSTAT = 0xC0;          // 清 SPIF 和 WCOL
    SPDAT = dat;
    while(!(SPSTAT & 0x80)); // 等待 SPIF 置位(B7)
}

// ---------------------- 颜色数据编码 ----------------------
void Ws2812_WriteColor(unsigned char wsGreen, unsigned char wsRed, unsigned char wsBlue)
{
    unsigned char i;

    for(i = 0; i < 8; i++)
    {
        if(wsGreen & (0x80 >> i))
        {
            wsColorBuf[i] = WS2812_CODE_1;
        }
        else
        {
            wsColorBuf[i] = WS2812_CODE_0;
        }
    }
    for(i = 0; i < 8; i++)
    {
        if(wsRed & (0x80 >> i))
        {
            wsColorBuf[8 + i] = WS2812_CODE_1;
        }
        else
        {
            wsColorBuf[8 + i] = WS2812_CODE_0;
        }
    }
    for(i = 0; i < 8; i++)
    {
        if(wsBlue & (0x80 >> i))
        {
            wsColorBuf[16 + i] = WS2812_CODE_1;
        }
        else
        {
            wsColorBuf[16 + i] = WS2812_CODE_0;
        }
    }
}

// ---------------------- 刷新输出 ----------------------
void Ws2812_Update(void)
{
    unsigned char i;

    // 发送前把 MOSI 拉低,保证 SPI 使能瞬间不产生高电平脉冲
    P34 = 0;

    for(i = 0; i < 24; i++)
    {
        SPI_SendByte(wsColorBuf[i]);
    }

    // 发送完成后把 MOSI 保持低,并等待 WS2812 复位
    P34 = 0;
    {
        unsigned int j;
        for(j = 0; j < 100; j++);   // 复位时间 >50us,按主频调整
    }
}

六、请您确认的信息

为了给出准确的 SPCTL 位配置,请提供:

  1. WS2812 的 DIN 实际接在哪个 P 脚(P3.3 还是 P3.4)?
  2. 您工程里 SPI_Init() 中 P_SW2 的 SPI_S 两位设成了多少?
  3. 请把 STC8H 手册《同步串行外设接口SPI》里 SPCTL 寄存器的位定义表截图发我,我据此确认“MISO/MOSI 可切换位”的位号,以及快速 SPI 下正确的主机模式配置值。

另外提醒:STC8H8K64U(A 版本)是普通 SPI,STC8H8K64U(B/C/D 版本)与 STC8H2K12U 才是快速 SPI。所以“同样的代码在 8K64U 上正常”这件事,取决于您那颗 8K64U 是哪个版本——如果它是老 A 版,两个芯片的 SPI 模块本来就不同,代码不能直接平移,这正说明了为什么换到 2K12U 后行为变了。


🔍 搜索 "STC8H2K12U WS2812 SPI MOSI 推挽输出"

⏱️ 查找用时 4.1s | 总回答用时 22.4s

QQ:3398500488
微信号:18106296592(小刘)
回复

使用道具 举报 送花

已绑定手机

9

主题

9

回帖

1955

积分

金牌会员

积分
1955
 楼主| 发表于 昨天 11:13 | 显示全部楼层

关于stc8h2k12u的硬件spi的高电平毛刺

stc8h2k12u的硬件spi每发送一个字节,后面都会跟一段高电平毛刺,即使加入miso引脚推挽输出为0也无济于事:


        SPCTL |=0x40;
        P34=0;       
        SPI_SendByte(0x55);
        SPI_SendByte(0x55);
        SPCTL &=~0x40;

如图波形。
经测试stc8h2k12u和stc8h2k17u都有这个问题,但是stc8h8k64u没有。
这对spi通讯来说无伤大雅,但是驱动ws2812的话就会令人十分头疼,有没有大佬知道解决方法吗
截图202611111107568601.jpg

点评

可以尝试 MOSI 引脚加反相器 + 软件内数据按位取反~  详情 回复 发表于 昨天 14:16
回复

使用道具 举报 送花

已绑定手机

124

主题

5139

回帖

1万

积分

超级版主

无情的代码机器

积分
11490
发表于 昨天 12:30 | 显示全部楼层
这个应该是这两系列增加了MISO与MOSI交换功能后引脚完全被SPI管控了,除非你失能SPI才能单独控制引脚。


记得之前有人提8H8K64U系列SPI工作时能单独操作MISO作其它用途,8H2K系列不行,同类问题。
对于SPI波形是正常的,只是想SPI使能期控制SPI引脚不行。

截图202611111229372541.jpg

三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

已绑定手机

74

主题

1028

回帖

3803

积分

论坛元老

初出茅庐&无甚本事

积分
3803
发表于 昨天 14:04 | 显示全部楼层
芯LYS 发表于 2026-11-11 08:44
手册已确认关键差异,问题根源找到了。
一、根本原因:STC8H2K12U 是“快速 SPI”,与 STC8H8K64U 不是同一 ...

版本问题还是得长记性,手册🉐多翻翻
偶尔看看玩玩
回复

使用道具 举报 送花

已绑定手机

74

主题

1028

回帖

3803

积分

论坛元老

初出茅庐&无甚本事

积分
3803
发表于 昨天 14:06 | 显示全部楼层
erci*** 发表于 2026-11-11 12:30
这个应该是这两系列增加了MISO与MOSI交换功能后引脚完全被SPI管控了,除非你失能SPI才能单独控制引脚。

没买到过A版本
但还是得留意
偶尔看看玩玩
回复

使用道具 举报 送花

已绑定手机

124

主题

5139

回帖

1万

积分

超级版主

无情的代码机器

积分
11490
发表于 昨天 14:16 | 显示全部楼层
lmgp*** 发表于 2026-11-11 11:13
stc8h2k12u的硬件spi每发送一个字节,后面都会跟一段高电平毛刺,即使加入miso引脚推挽输出为0也无济于事: ...

可以尝试 MOSI 引脚加反相器 + 软件内数据按位取反~
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-12 12:25 , Processed in 0.075911 second(s), 88 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表