STM32 USART串口DMA 接收和发送的源码详解!

发布者:sheng44最新更新时间:2018-09-11 来源: eefocus关键字:STM32  USART  串口  DMA 手机看文章 扫描二维码
随时随地手机看文章

硬件平台:STM32F103ZET6; 

开发环境:KEIL 4;

先说说应用通讯模式,串口终端的工作方式和迪文屏差不多,终端被动接受MCU发的指令,终端会偶尔主动发送一些数据给MCU(像迪文屏的触摸信息上传)。

串口DMA发送:

发送数据的流程:

前台程序中有数据要发送,则需要做如下几件事

1.      在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。

2.      将数据缓冲区内要发送的数据字节数赋值给发送DMA通道,(串口发送DMA和串口接收DAM不是同一个DMA通道)

3.      开启DMA,一旦开启,则DMA开始发送数据,说明一下:在KEIL调试好的时候,DMA和调试是不同步的,即不管Keil 是什么状态,DMA总是发送数据。

4.      等待发送完成标志位,即下面的终端服务函数中的第3点设置的标志位。或者根据自己的实际情况来定,是否要一直等待这个标志位,也可以通过状态机的方式来循环查询也可以。或者其他方式。

判断数据发送完成:

启动DMA并发送完后,产生DMA发送完成中断,在中断函数中做如下几件事:

1. 清DMA发送完成中断标志位

2. 关闭串口发送DMA通道

3. 给前台程序设置一个软件标志位,说明数据已经发送完毕

 

串口DMA接收:

接收数据的流程:

串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。

 

判断数据数据接收完成:

       这里判断接收完成是通过串口空闲中断的方式实现,即当串口数据流停止后,就会产生IDLE中断。这个中断里面做如下几件事:

1.      关闭串口接收DMA通道,2点原因:1.防止后面又有数据接收到,产生干扰。2.便于DMA的重新配置赋值,下面第4点。

2.      清除DMA 所有标志位

3.      从DMA寄存器中获取接收到的数据字节数

4.      重新设置DMA下次要接收的数据字节数,注意,这里是给DMA寄存器重新设置接收的计数值,这个数量只能大于或者等于可能接收的字节数,否则当DMA接收计数器递减到0的时候,又会重载这个计数值,重新循环递减计数,所以接收缓冲区的数据则会被覆盖丢失。

5.  开启DMA通道,等待下一次的数据接收,注意,对DMA的相关寄存器配置写入,如第4条的写入计数值,必须要在关闭DMA的条件进行,否则操作无效。

说明一下,STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。


USART 和 DMA 硬件初始化配置

 

/*--- LumModule Usart Config ---------------------------------------*/

 

#define LUMMOD_UART                      USART3

 

#define LUMMOD_UART_GPIO                 GPIOC

 

#define LUMMOD_UART_CLK                  RCC_APB1Periph_USART3

 

#define LUMMOD_UART_GPIO_CLK        RCC_APB2Periph_GPIOC

 

#define LUMMOD_UART_RxPin               GPIO_Pin_11

 

#define LUMMOD_UART_TxPin               GPIO_Pin_10

 

#define LUMMOD_UART_IRQn                USART3_IRQn

 

#define LUMMOD_UART_DR_Base                  (USART3_BASE + 0x4)  //0x40013804

 

 

#define LUMMOD_UART_Tx_DMA_Channel      DMA1_Channel2

 

#define LUMMOD_UART_Tx_DMA_FLAG         DMA1_FLAG_GL2//DMA1_FLAG_TC2 | DMA1_FLAG_TE2 

 

#define LUMMOD_UART_Tx_DMA_IRQ          DMA1_Channel2_IRQn

 

#define LUMMOD_UART_Rx_DMA_Channel      DMA1_Channel3

 

#define LUMMOD_UART_Rx_DMA_FLAG         DMA1_FLAG_GL3//DMA1_FLAG_TC3 | DMA1_FLAG_TE3 

 

#define LUMMOD_UART_Rx_DMA_IRQ      DMA1_Channel3_IRQn

 

 

void Uart_Init(void)

 

{

 

    NVIC_InitTypeDef NVIC_InitStructure;

 

    GPIO_InitTypeDef GPIO_InitStructure;

 

    USART_InitTypeDef USART_InitStructure;

 

    /* System Clocks Configuration */

 

//= System Clocks Configuration ==============================//

 

 

    /* Enable GPIO clock */

 

    RCC_APB2PeriphClockCmd(LUMMOD_UART_GPIO_CLK ,  ENABLE ); // 开启串口所在IO端口的时钟

 

    /* Enable USART Clock */

 

    RCC_APB1PeriphClockCmd(LUMMOD_UART_CLK, ENABLE); // 开始串口时钟

 

  

//=NVIC_Configuration======================================//

 

 

    /* Configure the NVIC Preemption Priority Bits */

 

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);

 

    /* Enable the DMA Interrupt */

 

    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_Tx_DMA_IRQ;   // 发送DMA通道的中断配置

 

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     // 优先级设置

 

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

 

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

 

    NVIC_Init(&NVIC_InitStructure);

 

 

    /* Enable the USART Interrupt */

 

    NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_IRQn;     // 串口中断配置

 

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

 

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

 

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

 

    NVIC_Init(&NVIC_InitStructure);

 

  

//=GPIO_Configuration====================================================//

 

    GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);  // 我这里没有用默认IO口,所以进行了重新映射,这个可以根据自己的硬件情况配置选择

 

    /* Configure USART3 Rx as input floating */

 

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 串口接收IO口的设置

 

    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_RxPin;

 

    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);

 

    /* Configure USART3 Tx as alternate function push-pull */

 

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // 串口发送IO口的设置

 

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 这里设置成复用形式的推挽输出   

 

    GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_TxPin;

 

    GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);

 

    DMA_Uart_Init();   // 串口 DMA 配置

 

 

    /* USART Format configuration ------------------------------------------------------*/

 

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;    // 串口格式配置

 

    USART_InitStructure.USART_StopBits = USART_StopBits_1;

 

    USART_InitStructure.USART_Parity = USART_Parity_No;

 

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

 

    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

 

 

 

    /* Configure USART3 */

 

    USART_InitStructure.USART_BaudRate = 115200;  //  波特率设置

 

    USART_Init(LUMMOD_UART, &USART_InitStructure);

 

    /* Enable USART3 Receive and Transmit interrupts */

 

    USART_ITConfig(LUMMOD_UART, USART_IT_IDLE, ENABLE);  // 开启 串口空闲IDEL 中断

 

 

    /* Enable the USART3 */

 

    USART_Cmd(LUMMOD_UART, ENABLE);  // 开启串口

 

    /* Enable USARTy DMA TX request */

 

    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送

 

    USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收

 

}

 

 

void DMA_Uart_Init(void)

 

{

 

    DMA_InitTypeDef DMA_InitStructure;

 

   

 

    /* DMA clock enable */

 

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 开启DMA1时钟

 

//=DMA_Configuration===============================================//

 

/*--- LUMMOD_UART_Tx_DMA_Channel DMA Config ---*/

 

 

 

    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);                           // 关DMA通道

 

    DMA_DeInit(LUMMOD_UART_Tx_DMA_Channel);                                 // 恢复缺省值

 

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口发送数据寄存器

 

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Tx_Buf;         // 设置发送缓冲区首地址

 

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 设置外设位目标,内存缓冲区 ->外设寄存器

 

    DMA_InitStructure.DMA_BufferSize = LUMMOD_TX_BSIZE;                     // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值

 

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的

 

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整

 

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节

 

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节

 

    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式

 

    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置

 

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式

 

    DMA_Init(LUMMOD_UART_Tx_DMA_Channel, &DMA_InitStructure);               // 写入配置

 

    DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);                                 // 清除DMA所有标志

 

    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // 关闭DMA

 

    DMA_ITConfig(LUMMOD_UART_Tx_DMA_Channel, DMA_IT_TC, ENABLE);            // 开启发送DMA通道中断

 

/*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/

 

 

 

    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);                           // 关DMA通道

 

    DMA_DeInit(LUMMOD_UART_Rx_DMA_Channel);                                 // 恢复缺省值

 

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口接收数据寄存器

 

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf;         // 设置接收缓冲区首地址

 

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                     // 设置外设为数据源,外设寄存器 -> 内存缓冲区

 

    DMA_InitStructure.DMA_BufferSize = LUMMOD_RX_BSIZE;                     // 需要最大可能接收到的字节数

 

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的

 

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整

 

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节

 

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节

 

    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式

 

    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置

 

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式

 

    DMA_Init(LUMMOD_UART_Rx_DMA_Channel, &DMA_InitStructure);               // 写入配置

 

    DMA_ClearFlag(LUMMOD_UART_Rx_DMA_FLAG);                                 // 清除DMA所有标志

 

    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE);                            // 开启接收DMA通道,等待接收数据

 

 

}

 

void BSP_Init(void)

 

{

 

    Uart_Init();

 

}

 

//============================================================//

 

DMA 发送应用源码

 

void DMA1_Channel2_IRQHandler(void)

 

{

 

    if(DMA_GetITStatus(DMA1_FLAG_TC2))

 

    {

 

        LumMod_Uart_DAM_Tx_Over();

 

    }

 

}

 

void LumMod_Uart_DAM_Tx_Over(void)

 

{

 

    DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);         // 清除标志

 

    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);   // 关闭DMA通道

 

    OSMboxPost(mbLumModule_Tx, (void*)1);           // 设置标志位,这里我用的是UCOSII ,可以根据自己的需求进行修改

 

}

 

 

 

void LumMod_Cmd_WriteParam( uint8 sample_num, uint8 *psz_param )

 

{

 

    uint8 err;

 

    uint8 LumMod_Tx_Index ;

 

 

 

    LumMod_Tx_Index = 0;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 1;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 2;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 3;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 4;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 5;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 6;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 7;

 

    LumMod_Tx_Buf[LumMod_Tx_Index++] = 8;

 

 

    LumMod_Uart_Start_DMA_Tx( LumMod_Tx_Index );

 

    OSMboxPend(mbLumModule_Tx, 0, &err);

 

}

 

 

void LumMod_Uart_Start_DMA_Tx(uint16_t size)

{

 

    LUMMOD_UART_Tx_DMA_Channel->CNDTR = (uint16_t)size; // 设置要发送的字节数目

 

    DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, ENABLE);        //开始DMA发送

 

}

 

//============================================================//

 

DMA 接收应用源码

 

void USART3_IRQHandler(void)

 

{

 

    if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)  // 空闲中断

 

    {

 

        LumMod_Uart_DMA_Rx_Data();

 

        USART_ReceiveData( USART3 ); // Clear IDLE interrupt flag bit

 

    }

 

}

 

void LumMod_Uart_DMA_Rx_Data(void)

 

{

 

    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);       // 关闭DMA ,防止干扰

 

    DMA_ClearFlag( LUMMOD_UART_Rx_DMA_FLAG );           // 清DMA标志位

 

    LumMod_Rx_Data.index = LUMMOD_RX_BSIZE - DMA_GetCurrDataCounter(LUMMOD_UART_Rx_DMA_Channel); //获得接收到的字节数

 

    LUMMOD_UART_Rx_DMA_Channel->CNDTR = LUMMOD_RX_BSIZE;    //  重新赋值计数值,必须大于等于最大可能接收到的数据帧数目

 

    DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE);        /* DMA 开启,等待数据。注意,如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有2种方式解决。

}


    1. 在重新开启接收DMA通道之前,将LumMod_Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。


    2. 建立双缓冲,在LumMod_Uart_DMA_Rx_Data函数中,重新配置DMA_MemoryBaseAddr 的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。*/


    OSMboxPost(mbLumModule_Rx,  LumMod_Rx_Buf); // 发送接收到新数据标志,供前台程序查询


关键字:STM32  USART  串口  DMA 引用地址:STM32 USART串口DMA 接收和发送的源码详解!

上一篇:STM32串口+DMA使用1
下一篇:STM32F0 定时器触发ADC,多通道采样、DMA传输数据的配置

推荐阅读最新更新时间:2024-03-16 16:13

ucosi操作系统移植到STM32
这个工作主要是根据网友的经验资料来学习移植的。总的来说需要下面几个参考资料。 ARM Cortex-m3权威指南 官方移植文档资料 网友移植成功经验资料 这三种资料在我的资源上传里面都能找到。 我在官网上下的是官方已经移植好的到STM32F103评估板的资料,很多部分已经实现了,所以需要改动的地方很少,不同的地方可以参考第三种网友移植成功经验资料,里面有详细说明。我下的是ucosii2.86版本。 下面说说移植过程: 移植主要涉及到两个源文件,os_cpu_c.c和os_cpu_a.asm,os_cpu.h。其他文件是ucosii核心文件,不需要修改。还有两个配置文件app_cfg.h和os_cfg.h
[单片机]
stm32 死区
看STM32手册 高级定时器有这么一行 一直不理解deadtime什么意思 16-bit, motor control PWM timer with deadtime generation and emergency stop 死区,简单解释: 通常,大功率电机、变频器等,末端都是由大功率管、IGBT等元件组成的 H桥或3相桥。 每个桥的上半桥和下半桥是是绝对不能同时导通的,但高速的PWM驱动信号在达到功率元件的控制极时,往往会由于各种各样的原因产生延迟的效果,造 成某个半桥元件在应该关断时没有关断,造成功率元件烧毁。 死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后, 延迟一段时间再打开上半桥
[单片机]
STM32安全固件更新的安全挑战
固件更新是我们软件生命周期中必须面临的问题。软件生命周期中有多种原因让我们需要固件更新,而固件更新也会带来安全问题,如使用固件更新来攻击电子钱包。 STM32 已经提供了很多的安全技术来应对固件更新的安全挑战。在这里和大家一起探讨,如何利用 STM32 安全技术,软件以及硬件安全技术,进行安全的升级固件。 讲座内容既包括了固件更新的一般原理,以及安全固件更新的额外设计。 安全固件更新离不开安全启动。STM32 安全启动为 STM32 安全固件更新提供了安全的运行环境。 什么是安全固件更新? 固件更新是指部分或者完全替换设备上的软件的操作,包括添加新应用,修改已有功能,或者去修复软件里的问题。安全固件更新,则是以安全的方
[单片机]
STM32 温度传感器 探究
温度传感器可以用来测量器件周围的温度(TA)。 温度传感器在内部和ADCx_IN16输入通道相连接,此通道把传感器输出的电压转换成数字值。 温度传感器模拟输入推荐采样时间是17.1μs。 当没有被使用时,传感器可以置于关电模式。 注意: 必须设置TSVREFE位激活内部通道:ADCx_IN16(温度传感器)和ADCx_IN17(VREFINT)的转换。 主要特征 ● 支持的温度范围:-40到125度 ● 精确度:+/- 1.5° C 读温度 为使用传感器: 1. 选择ADCx_IN16输入通道 2. 选择采样时间大于2.2 μs 3. 设置ADC控制寄存器2(ADC_CR2)的TSVREFE位,以唤醒关电模式下的温度
[单片机]
STM32教你如何编译出LIB文件
这个是生成LIB的技巧.也许大家在网上看到某些程序会发现,他们用这个方式来包含STM32的库文件: 而不是像DX32的例程那样一堆C: 那么,本技巧篇例程就是教大家怎么生成那个.LIB 的文件的. 首先打开本程序,大家看到的整个工程就只有库文件: 因为这是把STM32的函数库编译成库的形式,所以你只需要包含函数库就行. 然后注意一点,stm32f10x_conf.h 文件中,所有的include都要开放 #include stm32f10x_adc.h #include stm32f10x_bkp.h #include stm32f10x_can.h #include stm32f10x_crc.h #i
[单片机]
<font color='red'>STM32</font>教你如何编译出LIB文件
【嵌入式】STM32的库函数使用
使用编译环境为MDK5,烧录器将会使用JTAG和串口两种,芯片为STM32F103C8T6 零、前准备 1.MDK5编译环境 2.JTAG驱动或串口驱动 一、建立工程模板,写一个简单的小程序 1).创建工程目录 这里就会使用MDK创建工程了,由于STM32的工程已经比较大了,所以在工程目录下将会再创建多个目录,以便整理。 (说明一下,其实随便放都可以,只要工程都能包含以下说到的文件即可) 这里工程名就叫template吧(.代表工程目录): .CORE 存放芯片内核驱动文件; .FWLIB 使用库函数的话存放库函数; .OBJ 存放编译生成文件; .SYSTEM 存放系统代码; .USER 存放工程文件,应用程序等。 新建好
[单片机]
【嵌入式】<font color='red'>STM32</font>的库函数使用
使用STM32的SysTick实现精准延迟
使用SysTick的普通计数模式对延迟进行管理. static u8 fac_us=0;//us延时倍乘数 static u16 fac_ms=0;//ms延时倍乘数 //初始化延迟函数 void delay_init(u8 SYSCLK) { SysTick- CTRL&=0xfffffffb; //bit2清空,选择外部时钟*/ fac_us = SYSCLK/8; fac_ms = (u16)fac_us*1000; } //延时nms //SysTick- LOAD为24位寄存器,所以,最大延时为: //nms =0xffffff*8*1000/SYSCLK
[单片机]
基于串口透明传输的无线射频收发系统设计
目前市场上各类无线产品种类及应用越来越广泛,如何让广大学生、电子研发人员及一些小公司能快速在自己的产品中使用无线技术、降低无线技术的门槛,是本设计的初衷。对一些没有接触无线技术的人,从学到最后的设计应用需要比较长的时间。本文设计了一种基于无线收发芯片Si4432和单片机C8051F340的无线射频收发系统。基于串口透明传输,用户只需要了解一些串口指令即可方便实现无线收发。多种标准接口方便用户把模块嵌入到自己的产品中去。模块经过大量的试验、改进,能实现较远距离的稳定传输。 1、系统总体方案 无线射频收发系统结构框图如图1所示,由单片机C8051F340控制Si4432实现无线数据的收发。 发送模块中的C8051F340将数据
[单片机]
基于<font color='red'>串口</font>透明传输的无线射频收发系统设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved