GOOUUU ESP32-S3-CAM 果云科技开发板开发指南(一)(超详细!)Vscode+espidf 通过摄像头拍摄照片并存取到SD卡中,文末附源码

news2025/6/8 12:55:26

看到最近好玩的开源项目比较多,就想要学习一下esp32的开发,目前使用比较多的ide基本上是arduino、esp-idf和platformio,前者编译比较慢,后两者看到开源大佬的项目做的比较多,所以主要学习后两者。

本次使用的硬件是GOOUUU ESP32-S3-CAM,开发环境是espidf的5.4.1版本。

一:首先是环境配置

esp-idf和pio的环境都需要配置编译链,才能够更好的编译,我是在vscode里下载插件后,再去搭建好环境的,而由于espressif官方的库和依赖都在github上,所以在配环境上多少会遇到一些问题,大家可以自行去搜索环境配置,如果实在是解决不了的话,这里也给自己打一个小广告,咸鱼搜索flushddd,可以粉丝价帮助大家配置环境哈。

二:进入正题,本文从点灯开始,一步步深入到本文的最终目标

1.首先是放出最终的实验现象

最后的实验现象会在sd中找到我们拍到的照片,通过espidf的终端监视也可以看到拍摄到的图片被保存的信息。

三:首先开始点灯

首先是创建工程,通过idf的向导创建工程,我选择的框架是5.3.2的,虽然框架是5.3.2但是5.4.1的编译器也可以编译前者框架的代码,填写工程名称,地址,选择芯片目标,最后是选择基础框架,这里我们选择idf的sample-project

    

就是一个全新的工程,没有任何一个component的状态,只有main.c一个文件

开始点灯!

这里我们先看一下原理图,这里的led引脚是gpio43,记住这个数字,后面会用到。

如同stm32开发,想要编辑gpio口,也是要初始化一个结构体,创建代码led.c和led.h具体代码如下:


void led_init(void)
{
    gpio_config_t led_pin_config;//定义结构体
    led_pin_config.pin_bit_mask = 1<<LED_PIN;
    led_pin_config.mode = GPIO_MODE_OUTPUT;
    led_pin_config.pull_up_en = GPIO_PULLUP_DISABLE;
    led_pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
    led_pin_config.intr_type=GPIO_INTR_DISABLE;
    gpio_config(&led_pin_config);
}

上述为初始化代码,添加到led.c中,并且需要宏定义LED_PIN,并且添加到.h文件中。

#define LED_PIN 43

然后是控制LED的亮灭,以下就是控制LED亮灭的代码。

void led_on(void)
{    
    gpio_set_level(LED_PIN, 0);
}

void led_off(void)
{
    gpio_set_level(LED_PIN, 1);
}

和led.h的代码

#ifndef __LED_H
#define __LED_H
 
#include "driver/gpio.h"
#define LED_PIN 43

void led_init(void);
void led_on(void);
void led_off(void);

#endif

做完这些后,就需要创建frertos的任务,由于espidf是使用frertos进行开发的,所以在这里我们也使用任务来点灯,当然了,在main里while循环也是可以的。

void led_blink_task(void * param)
{
  
    while(1)
    {
    led_on();
    vTaskDelay(pdMS_TO_TICKS(500));
    led_off();
    vTaskDelay(pdMS_TO_TICKS(500));
    }
}


void app_main(void)
{
  led_init();
 xTaskCreatePinnedToCore(led_blink_task,"led",4096,NULL,3,NULL,1);
}

以上代码就是在main.c中的 全部代码,然后点击编译和烧录,没有意外的话就会看到板子上的蓝色小灯以0.5s为间隔在一闪一闪。

 2.点灯实现了之后,接下来需要实现的是SD的读取和文件系统的挂载。

通过原理图我们可知,我们的sd卡是一个单线的,数据线只通过一个data信号线即可读取,然后是SD_CMD和SD_CLK两根线,即可完成sd卡的控制。

 具体初始化代码如下:

sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    slot_config.width=1;
    slot_config.clk=SD_PIN_CLK;
    slot_config.cmd=SD_PIN_CMD;
    slot_config.d0=SD_PIN_D0;
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

在这里面我们需要做的就是定义好引脚,通过引脚分配图我们可知,cmd是gpio38,clk是39以及Data是40,这里记得去完成一个宏定义即可。

除此之外,还需要做的是挂载文件系统,初始化代码如下:

    esp_vfs_fat_sdmmc_mount_config_t mount_config ={
        .format_if_mount_failed = false,
        .max_files=5,
        .allocation_unit_size=16*1024,

     };

具体含义为,操作的最大文件数是5,分配的文件大小为16*1024字节,也就是16kb,

综上所述,完成初始化sd卡和挂载文件系统代码如下

   esp_vfs_fat_sdmmc_mount_config_t mount_config ={
        .format_if_mount_failed = false,
        .max_files=5,
        .allocation_unit_size=16*1024,

     };
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");
    ESP_LOGI(TAG, "Using SDMMC peripheral");
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    slot_config.width=1;
    slot_config.clk=SD_PIN_CLK;
    slot_config.cmd=SD_PIN_CMD;
    slot_config.d0=SD_PIN_D0;
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");
 sdmmc_card_print_info(stdout, card);

最后初始化完成了之后,会提示Filesystem mounted,如图所示。

 

在这里面我们也可以写一个测试代码,写入一个helloword.txt


 esp_err_t s_example_write_file(const char *path, char *data)
{
    ESP_LOGI(TAG, "Opening file %s", path);
    FILE *f = fopen(path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return ESP_FAIL;
    }
    fprintf(f, data);
    fclose(f);
    ESP_LOGI(TAG, "File written");

    return ESP_OK;
}
 const char *file_hello = MOUNT_POINT"/hello1.txt";
    char data[EXAMPLE_MAX_CHAR_SIZE];
    snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "HelloWorld", card->cid.name);
    ret = s_example_write_file(file_hello, data);
    if (ret != ESP_OK) {
        return;
    }

加入到app_main()函数里,编译烧录后,可以看到sd卡中出现了helloword.txt,具体内容为 helloword +你自己sd卡的名字。

3.最后,也就是camera

这里面我们需要把三者融合在一起,首先还是相机的初始化,这里先看原理图,我们这次使用的是OV2640型号的摄像头,camera的初始化一共需要14个引脚,其中有8根数据线,其余6根是信号线,然后在初始化之前,我们要打开esp32s3的psram,相机需要psram分配内存,将拍摄到的一帧图片存到psram中去。

这里我们先点开设置图样,或者通过espidf的cmd,定位到你创建工程的文件夹目录,通过命令

idf.py menuconfig也是可行的。在menuconfig里面找到ESP PSRAM并打开,勾选support for external ,SPI -connected RAM,spiram的模式就选择 quad,如果自己的板子后面在调试监视的时候报错,就更换成另外一种模式,自动检测使用SPIRAM类型,设置RAM的时钟速度为80MHZ,并且勾选Initialize SPI RAM during startup,这样就可以在初始化的过程中自己识别PSRAM的芯片类型并且在监视中显示出来了,这一步是初始化camera的关键,然后记得保存。

然后还有关键的一步,需要给现有的工程添加esp_camera的依赖库

通过idf的终端,输入指令:

idf.py add-dependency "espressif/esp32-camera"

即可在现有工程中添加相机库的依赖,引用esp_camera.h 

这里面为了确保psram确实已经得到了初始化,我们添加如下代码,显示如下:

 // 检测 PSRAM 存在性和大小
    if(esp_psram_get_size() == 0) {
        ESP_LOGE("BOOT", "PSRAM NOT DETECTED! Check hardware connection.");
        vTaskDelay(pdMS_TO_TICKS(1000));
        esp_restart(); // 自动重启
    }
    
    ESP_LOGI("BOOT", "PSRAM Size: %d KB", esp_psram_get_size() / 1024);
    
    // 初始化 PSRAM 缓存
    esp_psram_init();
    

和芯片型号esp32s3n16r8一致,r8即代表PSRAM为8MB,

接下来就是camera的初始化,通过原理图中的引脚进行配置,一定不要把引脚输错了,代码如下:

void bsp_camera_init(void)
{
camera_config_t cam_config = {
    .pin_pwdn = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sccb_scl = CAM_PIN_SIOC,
    .pin_sccb_sda = CAM_PIN_SIOD,
    
    .pin_d7=CAM_PIN_D7,
    .pin_d6=CAM_PIN_D6,
    .pin_d5=CAM_PIN_D5,
    .pin_d4=CAM_PIN_D4,
    .pin_d3=CAM_PIN_D3,
    .pin_d2=CAM_PIN_D2,
    .pin_d1=CAM_PIN_D1,
    .pin_d0=CAM_PIN_D0,
    .pin_vsync=CAM_PIN_VYSNC,
    .pin_href=CAM_PIN_HREF,
    .pin_pclk=CAM_PIN_PCLK,
    .ledc_timer = LEDC_TIMER_1,
    .ledc_channel = LEDC_CHANNEL_1,
    .xclk_freq_hz=10000000,
    .pixel_format = PIXFORMAT_JPEG,
    .frame_size = FRAMESIZE_QVGA,
    .jpeg_quality = 10,
    .fb_count = 2,
    .fb_location = CAMERA_FB_IN_PSRAM,
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
    .sccb_i2c_port = 1,

    
};

esp_err_t err = esp_camera_init(&cam_config);
    if (err != ESP_OK)
    {
        ESP_LOGI(TAG,"Camera init failed with error 0x%x", err);
        
    }
      // 详细错误处理

}

这里的初始化还需要注意一些参数分别是:.ledc_timer = LEDC_TIMER_1,
    .ledc_channel = LEDC_CHANNEL_1,
    .xclk_freq_hz=10000000,
    .pixel_format = PIXFORMAT_JPEG,
    .frame_size = FRAMESIZE_QVGA,
    .jpeg_quality = 10,
    .fb_count = 2,

分别指的是使用时钟的通道,camera的时钟频率,拍摄照片的分辨率是frame_size,照片的质量以及rgb格式等等,具体可以自己设置,最后完成初始化

在app_main进行初始化,初始化完成后,如果正确配置了这些参数, 通过监视器可以看到camera的一些信息,如图所示

做完这些后,我们就可以将前面的三者加起来,首先是初始化led作为测试用,然后是初始胡sd卡和相机,最后是拍照的代码,代码如下:

static uint32_t get_max_file_number(void)
{
    DIR *dir = opendir("/sdcard");
    if (!dir)
    {
        ESP_LOGE(TAG, "Failed to open directory");
        return 0;
    }

    uint32_t max_number = 0;
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL)
    {
        ESP_LOGI(TAG, "Processing file: %s", entry->d_name);

        // 检查文件名长度
        size_t name_len = strlen(entry->d_name);
        if (name_len != 12)
        { // 改为12,因为PHOTO000.JPG是12个字符
            ESP_LOGD(TAG, "Skipping file with wrong length: %s (%d)", entry->d_name, name_len);
            continue;
        }

        // 检查前缀
        if (memcmp(entry->d_name, "PHOTO", 5) != 0)
        {
            ESP_LOGD(TAG, "Skipping file without PHOTO prefix: %s", entry->d_name);
            continue;
        }

        // 检查扩展名
        if (memcmp(entry->d_name + 8, ".JPG", 4) != 0)
        {
            ESP_LOGD(TAG, "Skipping non-JPG file: %s", entry->d_name);
            continue;
        }

        // 提取数字部分
        char num_str[4] = {0};
        memcpy(num_str, entry->d_name + 5, 3);

        uint32_t current_number;
        if (sscanf(num_str, "%lu", &current_number) == 1)
        {
            ESP_LOGI(TAG, "Found valid photo number: %lu from file %s", current_number, entry->d_name);
            if (current_number > max_number)
            {
                max_number = current_number;
                ESP_LOGI(TAG, "New maximum number: %lu", max_number);
            }
        }
        else
        {
            ESP_LOGW(TAG, "Failed to parse number from: %s", num_str);
        }
    }

    closedir(dir);
    ESP_LOGI(TAG, "Final maximum file number found: %lu", max_number);
    return max_number;
}


void camera_capture()
{
     char filename[32];               // 文件名缓冲区
  camera_fb_t *fb = esp_camera_fb_get();
      static uint32_t file_number = 0; // 文件编号
    // 获取SD卡中最大的文件编号
    file_number = get_max_file_number() + 1;
    ESP_LOGI(TAG, "Starting file number: %lu", file_number);

if (!fb) {
    ESP_LOGE("CAM", "Failed to capture image");
} else {
    ESP_LOGI("CAM", "Image captured: %d bytes", fb->len);
    if(fb)
    {
 snprintf(filename, sizeof(filename), "/sdcard/PHOTO%03lu.JPG",file_number++);

                 {
  // 分配JPEG缓冲区
                uint8_t *jpeg_buf = NULL;
                size_t jpeg_len = 0;

                // 将frame转换为JPEG
                bool converted = frame2jpg(fb,      // 输入frame
                                           92,         // JPEG质量(0-100)
                                           &jpeg_buf,  // 输出JPEG缓冲区
                                           &jpeg_len); // 输出JPEG长度

                if (!converted)
                {
                    ESP_LOGE(TAG, "JPEG conversion failed");
                    esp_camera_fb_return(fb);
                }
                else 
                {
                     FILE *file = fopen(filename, "wb");
                     if (file)
                 {
                    size_t written = fwrite(jpeg_buf, 1, jpeg_len, file);
                    fclose(file);

                    if (written == jpeg_len)
                    {
                        ESP_LOGI(TAG, "Saved %s (%d bytes)", filename, jpeg_len);
                    }
                    else
                    {
                        ESP_LOGE(TAG, "File write failed: %d/%d bytes", written, jpeg_len);
                    }
                    // 清理资源
                   free(jpeg_buf);              // 释放JPEG缓冲区
                   esp_camera_fb_return(fb); // 返回帧缓冲区
                 }

                }
    }
}
    esp_camera_fb_return(fb);
}
}

 上述代码实现了首先查找sd卡中的文件数量,然后添加照片的命名,拍摄到图片后将其转成jpeg的格式,当将照片添加到sd卡中后,最后释放内存。其中拍照是通过函数esp_camera_fb_get();实现的,可惜的是板子没有可以使用的按键,否则检测按键,我们可以通过按按键来实现拍照。

最后将每个函数依次实现,编译烧录就大功告成了!

你可以看到自己拍摄到的图片出现在sd卡中了!

全部的工程如下:

通过网盘分享的文件:esp32-s3-cam
链接: https://pan.baidu.com/s/1MWgmBimOhGoUzEfFXVIDTQ?pwd=came 提取码: came 
--来自百度网盘超级会员v4的分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全流程开源!高德3D贴图生成系统,白模一键生成真实感纹理贴图

导读 MVPainter 随着3D生成从几何建模迈向真实感还原&#xff0c;贴图质量正逐渐成为决定3D资产视觉表现的核心因素。我们团队自研的MVPainter系统&#xff0c;作为业内首个全流程开源的3D贴图生成方案&#xff0c;仅需一张参考图与任意白模&#xff0c;即可自动生成对齐精确…

html 滚动条滚动过快会留下边框线

滚动条滚动过快时&#xff0c;会留下边框线 但其实大部分时候是这样的&#xff0c;没有多出边框线的 滚动条滚动过快时留下边框线的问题通常与滚动条样式和滚动行为有关。这种问题可能出现在使用了自定义滚动条样式的情况下。 注意&#xff1a;使用方法 6 好使&#xff0c;其它…

数据通信与计算机网络——数据与信号

主要内容 模拟与数字 周期模拟信号 数字信号 传输减损 数据速率限制 性能 注&#xff1a;数据必须被转换成电磁信号才能进行传输。 一、模拟与数字 数据以及表示数据的信号可以使用模拟或者数字的形式。数据可以是模拟的也可以是数字的&#xff0c;模拟数据是连续的采用…

【LLM大模型技术专题】「入门到精通系列教程」LangChain4j与Spring Boot集成开发实战指南

LangChain4j和SpringBoot入门指南 LangChain4jLangchain4j API语言模型消息类型内存对象ChatMemory接口的主要实现设置 API 密钥SpringBoot Configuration配置ChatLanguageModelStreamingChatLanguageModel初始化ChatModel对象模型配置分析介绍说明通过JavaConfig创建ChatModel…

Vue3 GSAP动画库绑定滚动条视差效果 绑定滚动条 滚动条动画 时间轴

介绍 GSAP 用于创建高性能、可控制的动画效果。由 GreenSock 团队开发&#xff0c;旨在提供流畅、快速、稳定的动画效果&#xff0c;并且兼容各种浏览器。 提供了多个插件&#xff0c;扩展了动画的功能&#xff0c;如 ScrollTrigger&#xff08;滚动触发动画&#xff09;、Dra…

grafana-mcp-analyzer:基于 MCP 的轻量 AI 分析监控图表的运维神器!

还在深夜盯着 Grafana 图表手动排查问题&#xff1f;今天推荐一个让 AI 能“读图说话”的开源神器 —— grafana-mcp-analyzer。 想象一下这样的场景&#xff1a; 凌晨3点&#xff0c;服务器告警响起。。。你睁着惺忪的眼睛盯着复杂的监控图表 &#x1f635;‍&#x1f4ab;花…

【题解-洛谷】B3622 枚举子集(递归实现指数型枚举)

题目&#xff1a;B3622 枚举子集&#xff08;递归实现指数型枚举&#xff09; 题目描述 今有 n n n 位同学&#xff0c;可以从中选出任意名同学参加合唱。 请输出所有可能的选择方案。 输入格式 仅一行&#xff0c;一个正整数 n n n。 输出格式 若干行&#xff0c;每行…

(LeetCode 每日一题)3170. 删除星号以后字典序最小的字符串(贪心+栈)

题目&#xff1a;3170. 删除星号以后字典序最小的字符串 思路&#xff1a;贪心栈&#xff0c;时间复杂度0(n)。 对于每一个‘ * ’&#xff0c;优先选最右边的最小字符&#xff0c;才会使最终得到的字符串最小。 用栈&#xff0c;来记录每个字符的位置下标。细节看注释。 C版本…

使用 HTML + JavaScript 实现文章逐句高亮朗读功能

在这个信息爆炸的时代&#xff0c;我们每天都要面对大量的文字阅读。无论是学习、工作还是个人成长&#xff0c;阅读都扮演着至关重要的角色。然而&#xff0c;在快节奏的生活中&#xff0c;我们往往难以找到足够的安静时间专注于阅读。本文用 HTML JavaScript 实现了一个基于…

双碳时代,能源调度的难题正从“发电侧”转向“企业侧”

安科瑞刘鸿鹏 摘要 在“双碳”战略和能源结构转型的大背景下&#xff0c;企业储能电站逐步成为提升能源利用效率、增强用能韧性的重要手段。随着系统规模扩大与运行复杂度提升&#xff0c;如何对光伏、储能、负荷等流进行实时调控&#xff0c;成为智慧用能的关键。ACCU100微…

3. 简述node.js特性与底层原理

&#x1f63a;&#x1f63a;&#x1f63a; 一、Node.js 底层原理&#xff08;简化版&#xff09; Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时&#xff0c;底层核心由几部分组成&#xff1a; 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行&#xff0…

OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个用于在 GPU 上执行模板匹配的 TemplateMatching 对象。 该函数返回一个指向 TemplateMatching 的智能指针&#xff08;Ptr&#xff09;…

【Kubernetes】K8s 之 ETCD - 恢复备份

ETCD 是一个高可用的分布式键值存储&#xff0c;常用于存储配置信息和服务发现等。当系统出现故障或数据损坏时&#xff0c;能够快速恢复成先前的状态是维护系统稳定性的关键。ETCD 提供了备份和恢复功能&#xff0c;以确保数据持久性和可靠性&#xff0c;一起来看看如何操作吧…

RabbitMQ 学习

MQ 的相关概念 什么是 MQ MQ&#xff08;message queue&#xff09;&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。…

如何轻松、安全地管理密码(新手指南)

很多人会为所有账户使用相同、易记的密码&#xff0c;而且常常多年不换。虽然这样方便记忆&#xff0c;但安全性非常低。 您可能听说过一些大型网站的信息泄露事件&#xff0c;同样的风险也可能存在于您的WordPress网站中。如果有不法分子获取了访问权限&#xff0c;您的网站和…

AWS App Mesh实战:构建可观测、安全的微服务通信解决方案

摘要&#xff1a;本文详解如何利用AWS App Mesh统一管理微服务间通信&#xff0c;实现精细化流量控制、端到端可观测性与安全通信&#xff0c;提升云原生应用稳定性。 一、什么是AWS App Mesh&#xff1f; AWS App Mesh 是一种服务网格&#xff08;Service Mesh&#xff09;解…

9.axios底层原理,和promise的对比(2)

&#x1f63a;&#x1f63a;&#x1f63a; 和promise的对比 完全可以直接使用 Promise 来发 HTTP 请求&#xff0c;比如用原生 fetch Promise 就可以实现网络请求功能&#x1f447; ✅ 用 Promise fetch 的写法&#xff08;原生&#xff09; fetch(‘https://api.example.c…

用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战

用HTML5 Canvas打造交互式心形粒子动画&#xff1a;从基础到优化实战 引言 在Web交互设计中&#xff0c;粒子动画因其动态美感和视觉吸引力被广泛应用于节日特效、情感化界面等场景。本文将通过实战案例&#xff0c;详细讲解如何使用HTML5 Canvas和JavaScript实现一个「心之律…

【软件工具】批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项

批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项 1、操作步骤1-5&#xff1a; 安装与启动&#xff1a;安装成功后&#xff0c;在桌面或开始菜单找到软件图标&#xff0c;双击启动。 导入图片&#xff1a;进入软件主界面&#xff0c;点击 “导入图片” 按钮&a…

数据通信与计算机网络——数字传输

主要内容 数字到数字转换 线路编码 线路编码方案 块编码 扰动 模拟到数字转换 脉冲码调制&#xff08;PCM&#xff09; Delta调制&#xff08;DM&#xff09; 传输模式 并行传输 串行传输 一、数字到数字转换 将数字数据转换为数字信号涉及三种技术&#xff1a; 线…