mangolu 发表于 2021-3-21 20:38:07

求助STC8G1K17-20PIN工作在22.1184MHz的时候串口1收发出错

本帖最后由 mangolu 于 2021-3-21 20:40 编辑

STC8G1K17-20PIN,用串口助手发送数据到Uart1,然后返回。工作在11.0592MHz的时候,收发正常,但是工作在22.1184MHz的时候,接收到返回的数据隔一段时间是错误的,请大家帮分析下这是什么原因造成的:

这里是工作在22.1184MHz的时候,返回数据错误:


这里是程序下载报告:
正在检测目标单片机 ...
单片机型号: STC8G1K17-20/16PIN
固件版本号: 7.3.12U

当前芯片的硬件选项为:
. 内部IRC振荡器的频率: 22.109MHz
. 掉电唤醒定时器的频率: 35.050KHz
. 振荡器放大增益使能
. P3.2和P3.3与下次下载无关
. 上电复位时增加额外的复位延时
. 复位引脚用作普通I/O口
. 检测到低压时复位
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1188 mV (参考范围: 1100~1300mV)
. 内部安排测试时间: 2021年1月29日

单片机型号: STC8G1K17-20/16PIN
固件版本号: 7.3.12U

开始调节频率 ...                       
调节后的频率: 22.123MHz (0.022%)

正在重新握手 ... 成功                       
当前的波特率: 115200
正在擦除目标区域 ... 完成 !               
芯片出厂序列号 : F757C5AD162396
正在下载用户代码 ... 完成 !               
正在设置硬件选项 ... 完成 !               

更新后的硬件选项为:
. 内部IRC振荡器的频率: 22.123MHz
. 掉电唤醒定时器的频率: 35.050KHz
. 振荡器放大增益使能
. P3.2和P3.3与下次下载无关
. 上电复位时增加额外的复位延时
. 复位引脚用作普通I/O口
. 检测到低压时复位
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1188 mV (参考范围: 1100~1300mV)
. 内部安排测试时间: 2021年1月29日

. 芯片出厂序列号 : F757C5AD162396
单片机型号: STC8G1K17-20/16PIN
固件版本号: 7.3.12U

. 用户设定频率: 22.118MHz
. 调节后的频率: 22.123MHz
. 频率调节误差: 0.022%


操作成功 !(2021-03-21 20:17:53)
在这里请教下,多次下载会发现:
. 调节后的频率: 22.123MHz
. 频率调节误差: 0.022%
这个频率和误差有时会变化,这个是什么情况?


这里是工作在11.0592MHz,所有数据是正常的,并且经过长时间运行是没有任何问题的:


这里是程序下载报告:
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1188 mV (参考范围: 1100~1300mV)
. 内部安排测试时间: 2021年1月29日

单片机型号: STC8G1K17-20/16PIN
固件版本号: 7.3.12U

开始调节频率 ...                       
调节后的频率: 11.054MHz (-0.043%)

正在重新握手 ... 成功                       
当前的波特率: 115200
正在擦除目标区域 ... 完成 !               
芯片出厂序列号 : F757C5AD162396
正在下载用户代码 ... 完成 !               
正在设置硬件选项 ... 完成 !               

更新后的硬件选项为:
. 内部IRC振荡器的频率: 11.054MHz
. 掉电唤醒定时器的频率: 35.050KHz
. 振荡器放大增益使能
. P3.2和P3.3与下次下载无关
. 上电复位时增加额外的复位延时
. 复位引脚用作普通I/O口
. 检测到低压时复位
. 低压检测门槛电压 : 2.00 V
. 上电复位时,硬件不启动内部看门狗
. 上电自动启动内部看门狗时的预分频数为 : 256
. 空闲状态时看门狗定时器停止计数
. 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
. 下次下载用户程序时,将用户EEPROM区一并擦除
. 下次下载用户程序时,没有相关的端口控制485
. 下次下载时不需要校验下载口令
. 内部参考电压: 1188 mV (参考范围: 1100~1300mV)
. 内部安排测试时间: 2021年1月29日

. 芯片出厂序列号 : F757C5AD162396
单片机型号: STC8G1K17-20/16PIN
固件版本号: 7.3.12U

. 用户设定频率: 11.059MHz
. 调节后的频率: 11.054MHz
. 频率调节误差: -0.043%


操作成功 !(2021-03-21 20:22:00)

下面是代码,初始化定时器是用官方软件生成的,这个发送接收代码在其他单片机上没有发现有问题:
#include "stdint.h"
#include "common.h"
#include "config.h"
#include "initial.h"
#include "version.h"
#include "STC8xxxx.H"

/** 定义串口缓存大小 */
#define UART_BUFFER_SIZE 64

uint8_t idata u8Uart_Buffer;        // 串口缓存
uint8_t u8Front = 0;        // 读指针
uint8_t u8Rear = 0;                // 写指针
uint8_t u8Count = 0;        // 缓存存储字节计数
uint8_t u8TX_State;                // 发送状态,0表示不在发送状态,1表示在发送状态

void Uart1_Init_1(void);
void Uart1_Init_2(void);
void Uart_Send_Start(void);

void main(void) {

        /** 初始化端口 */
        P3M0 = 0x00;        // 准双向口
        P3M1 = 0x00;

        /** 初始化串口 */
        Uart1_Init_1();

        /** 设置串口开始工作 */
        ES = 1; // 开启串口1中断
        EA = 1; // 开启全局中断

        while(1) {
                Uart_Send_Start();
        }
}

/** 串口初始化函数 */
void Uart1_Init_1(void) {        //115200bps@11.0592MHz
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x40;                //定时器1时钟为Fosc,即1T
        AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                //设定定时器1为16位自动重装方式
        TL1 = 0xE8;                //设定定时初值
        TH1 = 0xFF;                //设定定时初值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1
}

/** 串口初始化函数 */
void Uart1_Init_2(void) {        //115200bps@22.1184MHz
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x40;                //定时器1时钟为Fosc,即1T
        AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                //设定定时器1为16位自动重装方式
        TL1 = 0xD0;                //设定定时初值
        TH1 = 0xFF;                //设定定时初值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1
}

/** 串口发送数据,使用中断发送,要发送一个数据以产生中断 */
void Uart_Send_Start(void) {
        if(u8Count > 63) {
                // 出错处理
        } else {
                if((u8Count > 0) && (u8TX_State == 0)) {        // 缓存有数据,并且不在发送状态,此时要进入发送状态
                        SBUF = u8Uart_Buffer;
                        u8Count --;
                        u8Front ++;
                        u8TX_State = 1;
                }
        }
}

/** 串口1中断服务函数 */
void Uart_ISR(void) interrupt UART1_VECTOR {
        if(RI == 1) {   // 接收中断
                RI = 0;    // 清接收中断,必需手动清除

                /** 接收中断表示数据接收到,取串口缓存寄存器的值 */
                u8Uart_Buffer = SBUF;
                u8Rear ++;
                u8Count ++;
                u8Rear &= 0x3F;        // 0-63
        }

        if(TI == 1) {   // 发送中断
                TI = 0;    // 清发送中断,必需手动清除

                /** 发送中断表示数据发送完成 */
                /** 发送中断表示数据发送完成,清忙标志 */
                if(u8Count > 0) {
                        SBUF = u8Uart_Buffer;
                        u8Count --;
                        u8Front ++;
                        u8Front &= 0x3F;        // 0-63
                } else {
                        u8TX_State = 0;
                }
        }
}

mangolu 发表于 2021-3-21 20:43:37

工作在11.0592MHz和22.1184MHz只是修改了串口初始化的定时器初值,并在下载软件界面更改工作频率,其他的代码没有动到,为什么工作在22.1184MHz时返回的数据会出现错误呢?

小李非刀 发表于 2021-3-21 21:48:05

楼主可以测试STC官网的例程,或者电话给STC技术支持给一个按你要求编写的例程。

饭桶 发表于 2021-3-21 21:52:12

接收中断里干的活太多了。

mangolu 发表于 2021-3-21 23:21:11

饭桶 发表于 2021-3-21 21:52
接收中断里干的活太多了。

同样是115200通讯速率,11.0592m频率正常,反倒22.1184m不正常,这个说不过去啊。并且中断里就执行那几步,别的比如modbus还要计算crc16校验,这个任务更重吧?

mangolu 发表于 2021-3-21 23:28:27

小李非刀 发表于 2021-3-21 21:48
楼主可以测试STC官网的例程,或者电话给STC技术支持给一个按你要求编写的例程。 ...

官网的在别的型号上试过,晚点我看看。
我疑惑的是更改工作频率就不正常,我这个程序在别家51工作是正常的。

su33691 发表于 2021-3-22 03:44:02

这是属于典型的临界变量问题。是代码的BUG.

mangolu 发表于 2021-3-22 03:56:24

本帖最后由 mangolu 于 2021-3-22 04:00 编辑

su33691 发表于 2021-3-22 03:44
这是属于典型的临界变量问题。是代码的BUG.

测试了官方的例程确实没有问题。我这里代码有什么问题,能否指点一下?谢谢!

我这个收发程序就是之前请教你们的,在N76E003上是没有问题的。

su33691 发表于 2021-3-22 05:49:18

一个或多个对象被中断和主循环同时访问,可能无法确保数据的完整性和可用性。
临界变量问题是编程的痛点。
楼主不是依靠编程来养家糊口,慢慢修炼吧。

饭桶 发表于 2021-3-22 07:32:58

mangolu 发表于 2021-3-21 23:21
同样是115200通讯速率,11.0592m频率正常,反倒22.1184m不正常,这个说不过去啊。并且中断里就执行那几步 ...

不管什么计算,包括MODBUS的,哪能在中断里做?

mPiDDR 发表于 2021-3-22 08:00:48

不要在循环里面进行发送工作。
SBUF = u8Uart_Buffer; 只在中断里填充。
在主循环里只填充发送缓冲区就可以。
这情况属于运行速度太快引起的 程序结构不合理 临界问题。

kundi 发表于 2021-3-22 09:14:42

我之前在STC8F系列上,串口接收数据的中断函数里,除了接收字符串,还有接收到字符串后进行判断,并调用对应函数,工作频率6MHZ。
后来,我把这个程序移植到STC16上面,频率升到了24M或32M,发现非常容易出错,表现为死机,特别是长字符串时。鼓捣几天都不能解决。最后,我就把判断字符串和调用对应函数的部分,放到大循环里,中断函数里只保留接收字符串,就改善了。

mangolu 发表于 2021-3-22 11:51:42

饭桶 发表于 2021-3-22 07:32
不管什么计算,包括MODBUS的,哪能在中断里做?

哦,不好意思,是我搞错了,确实不是在中断中计算的。但是我这几步处理应该不多吧?

mangolu 发表于 2021-3-22 11:54:17

su33691 发表于 2021-3-22 05:49
一个或多个对象被中断和主循环同时访问,可能无法确保数据的完整性和可用性。
临界变量问题是编程的痛点。
...

在这里缓存并不存在主循环和中断同时处的情况吧?

mangolu 发表于 2021-3-22 11:57:25

mPiDDR 发表于 2021-3-22 08:00
不要在循环里面进行发送工作。
SBUF = u8Uart_Buffer; 只在中断里填充。
在主循环里只填充发送缓冲区就可 ...

主循环和中断发送里通过u8TX_State变量判断,不存在两个同时发的情况吧?

mangolu 发表于 2021-3-22 12:06:51

这里是STC官方例程代码,在中断里分写读写收送缓存,然后在主循环中收缓存写入发缓存,这里中断不影响主循环里的操作吗?
void main(void)
{
        u8        i;

        GPIO_config();
        UART_config();
        EA = 1;

        while (1)
        {
                delay_ms(1);
                if(COM1.RX_TimeOut > 0)                //超时计数
                {
                        if(--COM1.RX_TimeOut == 0)
                        {
                                if(COM1.RX_Cnt > 0)
                                {
                                        for(i=0; i<COM1.RX_Cnt; i++)        TX1_write2buff(RX1_Buffer);        //收到的数据原样返回
                                }
                                COM1.RX_Cnt = 0;
                        }
                }
        }
}


void UART1_int (void) interrupt UART1_VECTOR
{
        if(RI)
        {
                RI = 0;
                if(COM1.B_RX_OK == 0)
                {
                        if(COM1.RX_Cnt >= COM_RX1_Lenth)        COM1.RX_Cnt = 0;
                        RX1_Buffer = SBUF;
                        COM1.RX_TimeOut = TimeOutSet1;
                }
        }

        if(TI)
        {
                TI = 0;
                if(COM1.TX_read != COM1.TX_write)
                {
                       SBUF = TX1_Buffer;
                        if(++COM1.TX_read >= COM_TX1_Lenth)                COM1.TX_read = 0;
                }
                else        COM1.B_TX_busy = 0;
        }
}

mangolu 发表于 2021-3-22 13:29:41

mPiDDR 发表于 2021-3-22 08:00
不要在循环里面进行发送工作。
SBUF = u8Uart_Buffer; 只在中断里填充。
在主循环里只填充发送缓冲区就可 ...

你这个是对的,经过把主循环里发送判断改成:

/** 串口发送数据 */
void Uart_Send_Start(void) {
        if(u8Count > 63) {
                // 出错处理
        } else {
                if((u8Count > 0) && (u8TX_State == 0)) {        // 缓存有数据,并且不在发送状态,此时要进入发送状态
                        u8TX_State = 1;        // 设置为发送状态
                        TI = 1;        // 产生一个发送中断以进入发送状态
                }
        }
}

主循环不进行任何发送操作,有数据要发送,只产生一个发送中断,现在测试一切正常。

ilikemcu 发表于 2021-3-22 13:54:13

今天测试刚刚买到的STC8G1K17A/SOP8,原来的程序和硬件使用的是STC15F101W,只用了4个IO,查看了GPIO设置,完全一样,更换了头文件,重新编译,程序代码都是一样的字节数,在串口下载界面上看十六进制代码完全一样,烧录也是设置一样的时钟,结果诡异的事情发生了.................

现在的STC8G1K,速度比15F的快不少,我的代码初始GPIO,主循环就是不断改变GPIO的高低电平,然后中间插入nop延时,没有任何其他的计算和外设硬件的设置应用,一模一样的代码,一模一样的时钟,但是输出速度快了大概30%,奇怪得很{:titter:}
好在应用要求不需要很准确,所以先用着吧{:lol:},估计这2款STC51实际内核不一样了,虽然都叫STC

mangolu 发表于 2021-3-22 14:09:42

ilikemcu 发表于 2021-3-22 13:54
今天测试刚刚买到的STC8G1K17A/SOP8,原来的程序和硬件使用的是STC15F101W,只用了4个IO,查看了GPIO设置, ...

之前看过,好像跟15F系列有几个寄存器名称不一样
页: [1]
查看完整版本: 求助STC8G1K17-20PIN工作在22.1184MHz的时候串口1收发出错