专栏
标签
如何使用AI工具加速Sysblock建模?
技术分享
发布于 2025-08-06 16:58:25
查看 18过去300天

为实现增量式现编码器解码模块功能,应用于电机控制案例。由于基于模型的建模方式构建的模型比较复杂,不便于理解,所以使用AI工具进行模型优化工作,本篇文档用于记录AI工具如何加速建模过程。

编码器原理介绍:

增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小1。

23.png
24.png
25.png

工作原理

  1. 构造基础:增量式编码器主要由码盘、敏感元件和计数电路组成。码盘边缘开有相等角度的缝隙,分为透明和不透明部分,敏感元件一般为光电系统或磁电系统。
  2. 脉冲生成:当码盘随工作轴一起转动时,每转过一个缝隙就会产生一次光线的明暗变化或磁场的强弱变化,敏感元件将这种变化转换为电信号,再经整形放大,得到一定幅值和功率的电脉冲输出信号,脉冲数等于转过的缝隙数。
  3. 方向判别:通常增量式编码器会有 A、B 两路信号,它们在空间上有一定的相对位置关系,保证产生的信号在相位上相差 1/4 周期,即 90 度相位差1。通过比较 A 相和 B 相的先后顺序来判断旋转方向,若 A 相领先 B 相,则为正方向;若 A 相滞后 B 相,则为负方向。
  4. 零位参考:部分增量式编码器还带有一个 Z 相信号,称为零位信号或复位信号,编码器轴每转一圈,Z 相就会输出一个脉冲,可将其作为参考机械零位。

信号解码思路

  1. 方向解码:可以用 00、01、10 和 11 表示 A 和 B 相的高低状态,通过监测这些状态的顺序变化来判断方向。如顺时针时状态变化为00->01->11->10->00,逆时针时为00->10->11->01->00。
  2. 位置增量解码:定义一个变量表示位置值,每次检测到方向变化时更新该变量,如顺时针旋转时增加变量值,逆时针旋转时减少变量值,这样每次 A、B 相信号状态变化时都更新变量,即可得到当前位置的增量信息。
  3. 零位信号解码:若编码器带有 Z 相信号,当 Z 信号触发时,可将位置值归零,实现原点校准。
  4. 速度解码:通过记录单位时间内的脉冲数量,结合编码器的分辨率,计算出旋转速度。

模型搭建:

26.png
27.png
28.png
29.png

在ENC_unpack子系统中,通过边缘检测模块获取A的下降沿并配合B的高低电平状态来控制输出count的增减。

30.png

可以看出,虽然图形化建模展现的逻辑足够清晰,但是整体模型规模较大,且存在多处类型转化的问题,相较于文本编程不够高效。

AI工具优化

通过AI大模型(kimi,ChatGPT,豆包,Deepseek),帮我写一个增量式编码器的解码程序,输入ENC_A和ENC_B两个信号,输出count值,使用C代码编程

初次版本:

增量式编码器是一种常见的测量旋转位置和速度的传感器,它通常会输出两个相位差为 90 度的信号(ENC_A 和 ENC_B)。通过检测这两个信号的变化,可以判断编码器的旋转方向和旋转步数。
以下是一个简单的增量式编码器解码程序的 C 代码示例:

#include <stdio.h>

// 假设这两个函数用于读取 ENC_A 和 ENC_B 信号
// 在实际应用中,你需要根据硬件接口进行修改
int read_ENC_A() {
    // 这里简单模拟读取 ENC_A 信号,实际应从硬件引脚读取
    return 0; // 示例返回值
}

int read_ENC_B() {
    // 这里简单模拟读取 ENC_B 信号,实际应从硬件引脚读取
    return 0; // 示例返回值
}

// 编码器解码函数
int encoder_decode(int *count) {
    static int prev_A = 0;
    static int prev_B = 0;

    int current_A = read_ENC_A();
    int current_B = read_ENC_B();

    // 判断旋转方向并更新计数
    if (prev_A == 0 && current_A == 1) {
        if (prev_B == 0) {
            (*count)++; // 正转
        } else {
            (*count)--; // 反转
        }
    }

    prev_A = current_A;
    prev_B = current_B;

    return *count;
}

int main() {
    int count = 0;

    // 模拟多次读取编码器信号
    for (int i = 0; i < 10; i++) {
        int new_count = encoder_decode(&count);
        printf("当前计数: %d\n", new_count);
    }

    return 0;
}

代码说明:

  1. read_ENC_A() 和 read_ENC_B() 函数:这两个函数用于读取编码器的 ENC_A 和 ENC_B 信号。在实际应用中,你需要根据硬件接口进行修改,例如使用 GPIO 读取函数。
  2. encoder_decode() 函数:该函数用于解码编码器信号并更新计数。它使用静态变量 prev_A 和 prev_B 来记录上一次的信号状态。当检测到 ENC_A 信号从低电平变为高电平时,根据 ENC_B 信号的状态判断旋转方向,并更新计数。
  3. main() 函数:在 main 函数中,我们初始化计数变量 count 为 0,并模拟多次读取编码器信号。每次调用 encoder_decode() 函数后,打印当前的计数值。

注意事项:

  1. 该代码仅为示例,实际应用中需要根据硬件平台和具体需求进行修改。
  2. 在实际应用中,可能需要使用中断来检测编码器信号的变化,以提高响应速度。
  3. 编码器的分辨率和精度取决于编码器的型号和参数,需要根据实际情况进行调整。

迭代分析:

可以发现encoder_decode() 函数存在问题:该函数用于解码编码器信号并更新计数。当检测到 ENC_A 信号从低电平变为高电平时,根据 ENC_B 信号的状态判断旋转方向,并更新计数。仅检测了A信号,显然不对。所以需要再AI指令进行优化。指令为:添加ENC_B 信号的边缘检测

第一次迭代

下面是在之前代码基础上添加了 ENC_B 信号边缘检测的增量式编码器解码程序。该程序会同时对 ENC_A 和 ENC_B 信号的上升沿和下降沿进行检测,以更精确地判断编码器的旋转方向和更新计数值。

#include <stdio.h>

// 假设这两个函数用于读取 ENC_A 和 ENC_B 信号
// 在实际应用中,你需要根据硬件接口进行修改
int read_ENC_A() {
    // 这里简单模拟读取 ENC_A 信号,实际应从硬件引脚读取
    return 0; // 示例返回值
}

int read_ENC_B() {
    // 这里简单模拟读取 ENC_B 信号,实际应从硬件引脚读取
    return 0; // 示例返回值
}

// 编码器解码函数
int encoder_decode(int *count) {
    static int prev_A = 0;
    static int prev_B = 0;

    int current_A = read_ENC_A();
    int current_B = read_ENC_B();

    // 检测 ENC_A 上升沿
    if (prev_A == 0 && current_A == 1) {
        if (current_B == 0) {
            (*count)++; // 正转
        } else {
            (*count)--; // 反转
        }
    }
    // 检测 ENC_A 下降沿
    if (prev_A == 1 && current_A == 0) {
        if (current_B == 1) {
            (*count)++; // 正转
        } else {
            (*count)--; // 反转
        }
    }
    // 检测 ENC_B 上升沿
    if (prev_B == 0 && current_B == 1) {
        if (current_A == 1) {
            (*count)++; // 正转
        } else {
            (*count)--; // 反转
        }
    }
    // 检测 ENC_B 下降沿
    if (prev_B == 1 && current_B == 0) {
        if (current_A == 0) {
            (*count)++; // 正转
        } else {
            (*count)--; // 反转
        }
    }

    prev_A = current_A;
    prev_B = current_B;

    return *count;
}

int main() {
    int count = 0;

    // 模拟多次读取编码器信号
    for (int i = 0; i < 10; i++) {
        int new_count = encoder_decode(&count);
        printf("当前计数: %d\n", new_count);
    }

    return 0;
}

代码说明:

1). 信号读取函数
read_ENC_A() 和 read_ENC_B() 函数模拟了从编码器读取 ENC_A 和 ENC_B 信号的操作。在实际应用中,你需要根据硬件平台(如单片机)的 GPIO 接口函数来实现真实的信号读取。

2). 编码器解码函数 encoder_decode()

  • 函数中使用静态变量 prev_A 和 prev_B 来记录上一次读取到的 ENC_A 和 ENC_B 信号状态。
  • 通过比较上一次和当前的信号状态,检测 ENC_A 和 ENC_B 信号的上升沿和下降沿。
  • 根据不同信号边沿以及另一路信号的当前状态,判断编码器的旋转方向,进而对计数值 count 进行增加或减少操作。

3). 主函数 main()

  • 初始化计数值 count 为 0。
  • 通过一个简单的循环模拟多次读取编码器信号的过程,每次调用 encoder_decode() 函数更新计数值,并将更新后的计数值打印输出。

注意事项:

  1. 此代码只是一个基础的软件模拟示例,在实际硬件应用中,你需要使用对应硬件平台的 GPIO 库函数来实现 read_ENC_A() 和 read_ENC_B() 函数。
  2. 为了提高编码器信号检测的实时性和准确性,建议使用硬件中断来处理信号边沿检测,而非单纯依靠软件轮询的方式。不同硬件平台的中断配置方法不同,需要参考相应的开发文档。

迭代分析

本次迭代补充了ENC_B的信号检测,补齐了AB信号的检测功能,但是多个if判断的代码结构不够优化,并且输入输出中存在指针应用,均会影响代码在软件中的部署。再次对代码进行优化。优化指令:优化encoder_decode函数,使他更加高效,添加ENC_A和ENC_B两个变量输入,以及输出count值。

第二次迭代

以下是对 encoder_decode 函数进行优化后的代码,通过将 ENC_A 和 ENC_B 作为参数传入函数,避免在函数内部重复调用读取信号的函数,从而提高效率。同时,采用状态机的思想,通过位运算将当前和上一次的 ENC_A、ENC_B 信号状态组合成一个 4 位的状态值,利用该状态值来判断旋转方向,减少了大量的条件判断语句,进一步提升代码执行效率。

#include <stdio.h>

// 编码器解码函数
int encoder_decode(int *count, int ENC_A, int ENC_B) {
    static int prev_state = 0;  // 用于记录上一次的状态
    // 将当前的 ENC_A 和 ENC_B 信号状态组合成 2 位值
    int current_state = (ENC_A << 1) | ENC_B;
    // 将上一次和当前的状态组合成 4 位值
    int full_state = (prev_state << 2) | current_state;

    // 根据 4 位状态值判断旋转方向并更新计数
    switch (full_state) {
        case 0b0001:
        case 0b0111:
        case 0b1110:
        case 0b1000:
            (*count)++;  // 正转
            break;
        case 0b0010:
        case 0b1011:
        case 0b1101:
        case 0b0100:
            (*count)--;  // 反转
            break;
        default:
            // 其他状态不改变计数
            break;
    }

    // 更新上一次的状态
    prev_state = current_state;

    return *count;
}

int main() {
    int count = 0;
    // 模拟不同的 ENC_A 和 ENC_B 信号组合
    int test_cases[][2] = {
        {0, 0}, {0, 1}, {1, 1}, {1, 0},
        {0, 0}, {0, 1}, {1, 1}, {1, 0}
    };

    for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
        int ENC_A = test_cases[i][0];
        int ENC_B = test_cases[i][1];
        int new_count = encoder_decode(&count, ENC_A, ENC_B);
        printf("ENC_A: %d, ENC_B: %d, 当前计数: %d\n", ENC_A, ENC_B, new_count);
    }

    return 0;
}

代码说明:

1). encoder_decode 函数

  • 参数:接收一个指向计数值 count 的指针,以及当前的 ENC_A 和 ENC_B 信号值作为输入。
  • 状态记录:使用静态变量 prev_state 记录上一次的 ENC_A 和 ENC_B 组合状态。
  • 状态组合:通过位运算将当前的 ENC_A 和 ENC_B 组合成 2 位的 current_state,再与 prev_state 组合成 4 位的 full_state。
  • 旋转方向判断:利用 switch 语句根据 full_state 的值判断编码器的旋转方向,进而更新计数值。
  • 状态更新:将 current_state 更新为 prev_state,以便下一次判断使用。

2). main 函数

  • 初始化计数值 count 为 0。
  • 定义一个二维数组 test_cases 来模拟不同的 ENC_A 和 ENC_B 信号组合。
  • 遍历 test_cases 数组,每次调用 encoder_decode 函数进行解码,并打印当前的 ENC_A、ENC_B 信号值以及更新后的计数值。

优化点总结:

  1. 减少函数调用:将 ENC_A 和 ENC_B 作为参数传入函数,避免在函数内部重复调用读取信号的函数,提高效率。
  2. 状态机优化:使用位运算和 switch 语句代替多个 if-else 条件判断,减少代码的分支判断,提升执行效率。
    通过这些优化,代码在处理编码器信号时会更加高效。

迭代分析

本次迭代基本响应了以上需求,再次优化,消除Count的指针变量。迭代指令:尝试不使用指针来实现功能,

第三次迭代

下面是不使用指针来实现增量式编码器解码功能的代码。在这个实现中,encoder_decode 函数不再接收指针作为参数,而是直接返回更新后的计数值。

#include <stdio.h>

// 编码器解码函数
int encoder_decode(int count, int ENC_A, int ENC_B) {
    static int prev_state = 0;
    // 组合当前的 ENC_A 和 ENC_B 信号状态
    int current_state = (ENC_A << 1) | ENC_B;
    // 组合上一次和当前的状态成一个 4 位值
    int full_state = (prev_state << 2) | current_state;

    // 根据 4 位状态值判断旋转方向并更新计数
    switch (full_state) {
        case 0b0001:
        case 0b0111:
        case 0b1110:
        case 0b1000:
            count++;  // 正转
            break;
        case 0b0010:
        case 0b1011:
        case 0b1101:
        case 0b0100:
            count--;  // 反转
            break;
        default:
            // 其他状态不改变计数
            break;
    }

    // 更新上一次的状态
    prev_state = current_state;

    return count;
}

int main() {
    int count = 0;
    // 模拟不同的 ENC_A 和 ENC_B 信号组合
    int test_cases[][2] = {
        {0, 0}, {0, 1}, {1, 1}, {1, 0},
        {0, 0}, {0, 1}, {1, 1}, {1, 0}
    };

    for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
        int ENC_A = test_cases[i][0];
        int ENC_B = test_cases[i][1];
        count = encoder_decode(count, ENC_A, ENC_B);
        printf("ENC_A: %d, ENC_B: %d, 当前计数: %d\n", ENC_A, ENC_B, count);
    }

    return 0;
}

代码说明:

1). encoder_decode 函数:

  • 参数:接收当前的计数值 count、ENC_A 和 ENC_B 信号值作为输入。
  • 状态记录:使用静态变量 prev_state 记录上一次的 ENC_A 和 ENC_B 组合状态。
  • 状态组合:通过位运算将当前的 ENC_A 和 ENC_B 组合成 2 位的 current_state,再与 prev_state 组合成 4 位的 full_state。
  • 旋转方向判断:利用 switch 语句根据 full_state 的值判断编码器的旋转方向,进而更新计数值。
  • 状态更新:将 current_state 更新为 prev_state,以便下一次判断使用。
    返回值:返回更新后的计数值。

2). main 函数:

  • 初始化计数值 count 为 0。
  • 定义一个二维数组 test_cases 来模拟不同的 ENC_A 和 ENC_B 信号组合。
  • 遍历 test_cases 数组,每次调用 encoder_decode 函数进行解码,并将返回的更新后的计数值重新赋值给 count,然后打印当前的 ENC_A、ENC_B 信号值以及更新后的计数值。
    这种实现方式避免了使用指针,使得函数的调用和逻辑更加直观,同时保持了使用状态机优化后的高效性。

尝试部署:

复制代码

拖入Cfunction组件,将AI生成的代码函数encoder_decode复制粘贴到模块中,并将main函数中的47行,函数的调用也复制到模块里,如下所示。

31.png

添加端口

按AI生成的代码中定义的端口名称和数据类型进行设置,需要注意的是,因为count计数未使用指针变量,所以将其作为输入输出端口进行使用,并在模型外围构造回传函数进行数值迭代操作。

32.png

运行仿真查看结果

33.png

添加的C代码运行无报错,但是输出为0。

迭代分析

初步拆测是因为内部存在静态变量导致的,再次使用AI工具优化代码,优化指令:尝试去除静态变量 prev_state和指针的使用,转为输入输出端口进行数据传递

第四次迭代

以下是去除静态变量 prev_state 和指针使用,完全通过输入输出端口进行数据传递来实现增量式编码器解码功能的代码:

#include <stdio.h>

// 编码器解码函数
int encoder_decode(int count, int prev_A, int prev_B, int ENC_A, int ENC_B) {
    // 组合上一次的 ENC_A 和 ENC_B 信号状态
    int prev_state = (prev_A << 1) | prev_B;
    // 组合当前的 ENC_A 和 ENC_B 信号状态
    int current_state = (ENC_A << 1) | ENC_B;
    // 组合上一次和当前的状态成一个 4 位值
    int full_state = (prev_state << 2) | current_state;

    // 根据 4 位状态值判断旋转方向并更新计数
    switch (full_state) {
        case 0b0001:
        case 0b0111:
        case 0b1110:
        case 0b1000:
            count++;  // 正转
            break;
        case 0b0010:
        case 0b1011:
        case 0b1101:
        case 0b0100:
            count--;  // 反转
            break;
        default:
            // 其他状态不改变计数
            break;
    }

    return count;
}

int main() {
    int count = 0;
    int prev_A = 0;
    int prev_B = 0;
    // 模拟不同的 ENC_A 和 ENC_B 信号组合
    int test_cases[][2] = {
        {0, 0}, {0, 1}, {1, 1}, {1, 0},
        {0, 0}, {0, 1}, {1, 1}, {1, 0}
    };

    for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
        int ENC_A = test_cases[i][0];
        int ENC_B = test_cases[i][1];
        count = encoder_decode(count, prev_A, prev_B, ENC_A, ENC_B);
        printf("ENC_A: %d, ENC_B: %d, 当前计数: %d\n", ENC_A, ENC_B, count);
        prev_A = ENC_A;
        prev_B = ENC_B;
    }

    return 0;
}

代码说明:

1). encoder_decode 函数

  • 参数:count:当前的计数值。prev_A 和 prev_B:上一次的 ENC_A 和 ENC_B 信号值。ENC_A 和 ENC_B:当前的 ENC_A 和 ENC_B 信号值。
  • 状态组合:
    先将 prev_A 和 prev_B 组合成 prev_state,再将 ENC_A 和 ENC_B 组合成 current_state,最后将两者组合成一个 4 位的 full_state。
  • 旋转方向判断:
    使用 switch 语句根据 full_state 的值判断编码器的旋转方向,相应地对计数值 count 进行增加或减少操作。
  • 返回值:返回更新后的计数值。

2). main 函数

  • 初始化:初始化计数值 count 为 0,同时初始化 prev_A 和 prev_B 为 0。
  • 模拟信号处理:定义一个二维数组 test_cases 来模拟不同的 ENC_A 和 ENC_B 信号组合。遍历 test_cases 数组,每次调用 encoder_decode 函数进行解码,并将返回的更新后的计数值重新赋值给 count。打印当前的 ENC_A、ENC_B 信号值以及更新后的计数值。更新 prev_A 和 prev_B 为当前的 ENC_A 和 ENC_B 值,以便下一次调用 encoder_decode 函数时使用。
    通过这种方式,完全避免了静态变量和指针的使用,所有数据都通过函数参数进行传递,代码的可读性和可维护性得到了提升。

尝试部署

将encoder_decode函数代码复制到cfunction中,并对输入输出变量进行配置

34.png

需要注意的是:由于prev_A 和 prev_B是ENC_A和ENC_B的上一时刻值,可以将ENCAB变量配置为输入输出IO类型,并在框图环境中外部串联一个单位延迟形成环路。代替原本静态变量的功能。

35.png
36.png

再次优化,将ENC_A和ENC_B串联一个单位延迟后得到prev_A 和 prev_B,直接作为cfunction的输入,也可同样实现等效功能。

37.png
38.png

运行结果

点击仿真,观察cfunction仿真结果与原本框图模型的仿真结果。结果趋势上保持一致,但是在初值上cfunction零时刻为1,而框图模型仿真结果为0.

39.png

原因猜测是因为初始时刻执行了cfunction,导致初始0时刻的count值加了1.

40.png

因为电机控制的角度控制允许一定的误差,并且增量式编码器的角度值未引入Z信号时更加强调相对角度而不是绝对角度,所以初值的偏移可以忽略。
针对示例模型,可以将prev_A的单位延迟模块的初值设置为1,来规避结果不对齐的现象。

最终模型分析

在本案例中,使用AI工具对编码器解码程序进行了重建替代。使用更加高效的Cfunction的形式来实现功能。模型的对比总结见最下面的附件。

41.png

生成代码对比:第一段为Enc_Unpack_Block子系统生成的代码,第二段为Enc_Unpack_Cfunction子系统生成的代码以及外部C函数。

void Enc_Unpack_Block(MwbBool ENC_A, MwbBool ENC_B, MwbInt8 MWRunningMode, MwbInt32* count_out_int32, model329Emd*const model329GbMd, 
  struct model329Enc_unpack_blockB* localB, struct model329Enc_unpack_blockDw* localDw)
{
  localB->y = !ENC_A;
  localB->y_b = !ENC_A;
  localB->y_d = 0;
  localB->y_e = ENC_A > localB->y_d;
  if (MWRunningMode == 1)
  {
    localB->y_f = localDw->initCond != 0;
    localDw->yb = localB->y_e;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_f = localDw->yb;
    localDw->yb = localB->y_e;
  }
  localB->y_c = localB->y_e > localB->y_f;
  if (MWRunningMode == 1)
  {
    localB->y_j = localDw->initCond_i != 0;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_j = localB->y_m;
  }
  localB->y_l = 0;
  localB->y_m = ENC_A <= localB->y_l;
  localB->y_h = localB->y_m > localB->y_j;
  if (MWRunningMode == 1)
  {
    localB->y_q = localDw->initCond_p != 0;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_q = localB->y_t;
  }
  localB->y_s = 0;
  localB->y_t = ENC_B <= localB->y_s;
  localB->y_o = localB->y_t > localB->y_q;
  localB->y_v = localB->y_o && localB->y;
  localB->y_w = (localB->y_v);
  localB->y_y = localB->y_o && ENC_A;
  localB->y_z = (localB->y_y);
  localB->y_aa = (-1) * localB->y_z;
  localB->y_da = 0;
  localB->y_ea = ENC_B > localB->y_da;
  if (MWRunningMode == 1)
  {
    localB->y_ga = localDw->initCond_fa != 0;
    localDw->yb_ia = localB->y_ea;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_ga = localDw->yb_ia;
    localDw->yb_ia = localB->y_ea;
  }
  localB->y_ca = localB->y_ea > localB->y_ga;
  localB->y_ka = localB->y_ca && localB->y_b;
  localB->y_la = (localB->y_ka);
  localB->y_ma = (-1) * localB->y_la;
  localB->y_oa = localB->y_ca && ENC_A;
  localB->y_pa = (localB->y_oa);
  localB->y_ra = localB->y_h && ENC_B;
  localB->y_sa = (localB->y_ra);
  localB->y_ua = !ENC_B;
  localB->y_wa = localB->y_c && localB->y_ua;
  localB->y_xa = (localB->y_wa);
  localB->y_za = localB->y_c && ENC_B;
  localB->y_zb = (localB->y_za);
  localB->y_zc = (-1) * localB->y_zb;
  localB->y_ze = !ENC_B;
  localB->y_zg = localB->y_h && localB->y_ze;
  localB->y_zh = (localB->y_zg);
  localB->y_zi = (-1) * localB->y_zh;
  localB->y_zk = localB->y_sa + localB->y_zi + localB->y_zc + localB->y_xa + localB->y_pa + localB->y_ma + localB->y_aa + localB->y_w;
  
  if (MWRunningMode == 1)
  {
    localB->y_zm = localDw->initCond_zl;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_zm = *count_out_int32;
  }
  *count_out_int32 = localB->y_zm + localB->y_zk;
}
void Enc_Unpack_Cfunction(MwbInt8 MWRunningMode, MwbInt32* count_out_int32, model329Emd*const model329GbMd, struct model329Enc_unpack_cfunctionB* 
  localB, struct model329Enc_unpack_cfunctionDw* localDw)
{
  localB->y = (ENC_A);
  localB->y_a = (ENC_B);
  if (MWRunningMode == 1)
  {
    localB->y_b = localDw->initCond;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_b = *count_out_int32;
  }
  if (MWRunningMode == 1)
  {
    localB->y_d = localDw->initCond_c;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_d = localB->out_ENC_A;
  }
  if (MWRunningMode == 1)
  {
    localB->y_g = localDw->initCond_f;
  }
  else if (MWRunningMode == 3)
  {
    localB->y_g = localB->out_ENC_B;
  }
  {
    void Model329_SubSystem3_CFunction1_CFunction(MwbInt32* , MwbInt32* , MwbInt32* , const MwbInt32, const MwbInt32);
    localB->out_ENC_A = localB->y;
    localB->out_ENC_B = localB->y_a;
    *count_out_int32 = localB->y_b;
    Model329_SubSystem3_CFunction1_CFunction(&(localB->out_ENC_A), &(localB->out_ENC_B), &(*count_out_int32), localB->y_d, localB->y_g);
    
  }
}

void Model329_SubSystem3_CFunction1_CFunction(int* __temp_out_ENC_A, int* __temp_out_ENC_B, int* __temp_out_count, int prev_A, int prev_B)
{
int ENC_A= *__temp_out_ENC_A;
int ENC_B= *__temp_out_ENC_B;
int count= *__temp_out_count;

/***Cfunction Code Divsion***/
int encoder_decode(int count, int prev_A, int prev_B, int ENC_A, int ENC_B) {
    // 组合上一次的 ENC_A 和 ENC_B 信号状态
    int prev_state = (prev_A << 1) | prev_B;
    // 组合当前的 ENC_A 和 ENC_B 信号状态
    int current_state = (ENC_A << 1) | ENC_B;
    // 组合上一次和当前的状态成一个 4 位值
    int full_state = (prev_state << 2) | current_state;

    // 根据 4 位状态值判断旋转方向并更新计数
    switch (full_state) {
        case 0b0001:
        case 0b0111:
        case 0b1110:
        case 0b1000:
            count--;  //反转
            break;
        case 0b0010:
        case 0b1011:
        case 0b1101:
        case 0b0100:
            count++;  // 正转
            break;
        default:
            // 其他状态不改变计数
            break;
    }

    return count;
}

count = encoder_decode(count, prev_A, prev_B, ENC_A, ENC_B);


/***Cfunction Code Divsion***/
*__temp_out_ENC_A = ENC_A;
*__temp_out_ENC_B = ENC_B;
*__temp_out_count = count;
}

借助CCaller和C-Function此类支持外部C代码集成的模块。用户可以通过AI工具快速搭建出高效的功能实现逻辑,也可以辅助建模人员基于AI生成的代码来优化模型。例如以下优化结果,第一个为源模型,后面两个为优化后的模型:
1.符合编码器信号解码逻辑的框图

42.png

2.基于AI生成代码倒推实现的建模方式1

43.png

3.基于AI生成代码倒推实现的建模方式2

44.png

总结

相较于直接代码生成,使用AI工具生成的代码更具备可读性。但是基于Sysblock直接生成的C代码具备与模型图元的映射关系,在debug阶段各有优势。

从以上AI辅助建模的过程可以看出,AI工具在辅助图形化建模的过程中,针对单个明确输入输出的功能模块组件时可以快速准确的给出代码内容,并支持快速的改写,使得代码更加容易集成到框图建模环境中。

但是针对系统模型,AI提供的架构和系统结构往往无法生成符合框图建模逻辑的图形化模型。后续实际搭建后再做补充

所属专栏:Sysblock基础平台
产品信息:Sysblock 嵌入式代码生成工具
其他
附件 1 个附件(180kb)

全部回答

暂无数据
暂无数据
用户
和原帖交流更多问题细节吧,去
我要发帖 我要发帖
资料中心 资料中心
查看更多>
热门帖子 热门帖子
主要贡献者 主要贡献者
过去7天