STM32系统定时器SysTick

发布者:快乐阳光最新更新时间:2018-12-01 来源: eefocus关键字:STM32  系统定时器  SysTick 手机看文章 扫描二维码
随时随地手机看文章

1. SysTick系统定时器概述


学习完STM32的中断,下来就要学习STM32的定时器。就像电话最基本的功能是与人通话一样,定时器最基本的功能就是定时(STM32有些定时器的功能强大得超乎想象,当然不是今天要学的SysTick),定时器的使用步骤无非就是设置定时时间,然后等待超时,超时会触发中断或者设置某个标志位:若是触发中断自然要去执行中断处理函数,处理函数和中断源的绑定工作在启动文件的中断向量表已经明确,我们负责实现与之对应的中断处理函数即可;若是查询标志位则轮询访问该标志位,发生改变后再执行其他处理操作。


定时器涉及到中断,那就跟前面学习的EXTI、NVIC以及中断优先级等概念扯上关系了。系统定时器SysTick是属于CM3内核中的一个外设,相关寄存器内嵌在NVIC中,所有基于CM3内核的单片机都具有这个系统定时器,这使得软件(OS)在CM3单片机可以十分容易的移植。SysTick一般用于操作系统的产生时基功能,以维持操作系统的“心跳”。


2. SysTick系统定时器相关寄存器


下图摘自《Cortex™-M3技术参考手册.pdf》-P153 


这里写图片描述


标准库中core_cm3.h中对SysTick描述结构体放入封装与之对应:


typedef struct

{

  __IO uint32_t DHCSR;                        /*!< Offset: 0x00  Debug Halting Control and Status Register    */

  __O  uint32_t DCRSR;                        /*!< Offset: 0x04  Debug Core Register Selector Register        */

  __IO uint32_t DCRDR;                        /*!< Offset: 0x08  Debug Core Register Data Register            */

  __IO uint32_t DEMCR;                        /*!< Offset: 0x0C  Debug Exception and Monitor Control Register */

} CoreDebug_Type;


2.1 SysTick控制及状态寄存器(SysTick Control and Status Register)


这里写图片描述


COUNTFLAG: 若上次读取本寄存器后,SysTick已经计到0,则该位为1 


CLKSOURCE: 时钟源选择位,0表AHB / 8,1表处理器时钟AHB 


TICKINT: 1表SysTick倒数计数计到0时产生SysTick异常请求,0表计到0时无动作。也可以通过读取COUNTFLAG来确定计数器是否递减到0 


ENABLE: SysTick定时器使能位 


英文描述如下: 


这里写图片描述


2.2 SysTick重装载数值寄存器(SysTick Reload Value Register)


这里写图片描述


RELOAD: 当倒数计到0时,将被重装载的值 


英文描述如下: 


这里写图片描述


2.3 SysTick当前数值寄存器(SysTick Current Value Register)


这里写图片描述


CURRENT: 读取时返回当前计数的值,写它则使其清零,同时清除SysTick控制及状态寄存器中的COUNTFLAG状态标志位 


英文描述如下: 


这里写图片描述


2.4 SysTick校准数值寄存器(SysTick Calibration Value Register)


这里写图片描述


这个校准不常用,先不理会。 


英文描述如下: 


这里写图片描述
这里写图片描述


系统定时器SysTick有一个24Bit的向下的计数器,该计数器的值会被拷贝到重装载数值寄存器中,一般的配置是,当重装载数值寄存器的值递减到0的时候系统定时器就会产生一次中断,以此循环往复。


这里引入一个问题,计数器多久会递减1? 

 

这里写图片描述 


图为《STM32中文参考手册_V10.pdf》中时钟树。Cortex系统时钟即是指SysTick的时钟,很明显被设置为HCLK / 8,也就是9MHZ。再看外设库中对SysTick时钟源的设置是否为HCLK / 8? 


在Libraries\STM32F10x_StdPeriph_Driver\src\misc.c,竟然有专门用于设置SysTick的时钟源的函数,


void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)

{

    /* Check the parameters */

    assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));

    if (SysTick_CLKSource == SysTick_CLKSource_HCLK)

    {

        SysTick->CTRL |= SysTick_CLKSource_HCLK;

    }

    else

    {

        SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;

    }

}


参数取值为SysTick_CLKSource_HCLK表示时钟源设置为HCLK,取值SysTick_CLKSource_HCLK_Div8表将时钟源设置为HCLK/8。 


在前面的CLKSOURCE寄存器位也明确指明SysTick的时钟源为HCLK和HCLK / 8可选,然而在时钟树上却并没有体现时钟源可选这个关键信息,可见这是数据手册的漏洞,功能实现以寄存器的说明为主而非时钟树:SysTick的时钟源并非限定在HCLK / 8


定义在Libraries\CMSIS\CM3\CoreSupport\core_cm3.h中的SysTick_Config()的函数中,实现相以上关寄存器的配置,这是至关重要的一个函数,


static __INLINE uint32_t SysTick_Config(uint32_t ticks)

  /* SysTick计数器的最大值为24Bit,超出范围则返回 */

  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */


  /* 设置重装载寄存器 */

  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */


  /* 设置中断源(处理函数)的优先级 */

  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */


  /* 设置当前数值寄存器的值为0 */

  /* 设置SysTick的时钟源为HCLK=72MHz */

  /* 使能SysTick超时中断 */

  /* 使能SysTick */

  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */

  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 

                   SysTick_CTRL_TICKINT_Msk   | 

                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */

  return (0);                                                  /* Function successful */

}


注意这里将SysTick的时钟源设置为72MHZ,那么计数器减1间隔的时间为: (1 / 72MHz)s,其它相关配置项已经代码的注释中写出,读者可以参照源码阅读。


3. SysTick系统定时器的重装载寄存器数值

使用标准外设库编程的时候我们只需要调用SysTick_Config(uint32_t ticks)函数即可 ,形参ticks用来设置重装载寄存器的值,最大不可超过2的24次方,当重装载寄存器的值递减到0的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环。 


设置ticks的值等价于设置计时时间,中间有一个简单的换算过程:SysTick每递减1需要(1/72MHZ)s,那么定时1s则将ticks取值为72000000,定时1ms则ticks取值为72000,定时10us则ticks取值为720,定时1us则ticks取值为72,总归:


SysTick时钟源 / 1000   =>  定时1ms

SysTick时钟源 / 100000 =>  定时10us

SysTick时钟源 / 1000000    =>  定时1us


4. SysTick系统定时器中断优先级


在SysTick_Config()中还调用NVIC_SetPriority()函数用于设置SysTick的中断优先级,该函数也在core_m3.h中定义,原型如下:


/* 设置中断源的中断优先级 */

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

{

  if(IRQn < 0) {

    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */

  else {

    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */

}


参数一的类型IRQn_Type结构体,其原型描述了STM32所支持的系统异常和外部中断:


typedef enum IRQn

{

/******  Cortex-M3 Processor Exceptions Numbers ***************************************************/

  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */

  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */

  BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */

  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */

  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */

  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */

  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */

  SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */


/******  STM32 specific Interrupt Numbers *********************************************************/

  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */

  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */

  TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */

  RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */

  FLASH_IRQn     

  ...

}


可见系统异常的取值小于0,外部中断的取值则大于0。 


参数二则表示优先级。 


函数首先首先判断IRQn的大小,若小于0则表示系统异常,系统异常的优先级由内核外设SCB的寄存器SHPRx控制,若大于0则是外部中断,外部中断的优先级由内核外设NVIC中的IPx寄存器控制。有关SHPRx需要参考《Cortex-M3内核编程手册》相关章节。 


Systick属于内核外设,跟普通外设的中断优先级存在差别,它并没有抢占优先级和子优先级的说法。SysTick_Config()函数默认其中断优先级设置为15,它在内核外设中的优先级是最低的,若要修改优先级则通过NVIC_SetPriority()函数的参数二,范围为0~15。 


这里引入了内核外设和片上外设优先级的问题:若系统的中断优先分组为2,内核外设SysTick优先级为函数库默认配置的15,片上外设某个中断源的抢占优先级为1,子优先级也为1,那它们之间的中断优先级该如何比较,若同时发生中断请求,谁的中断处理函数先得到响应? 


NVIC的中断优先级分组不仅对片上外设有效,同样对内核外设也有效。比较方法是:将SysTick的优先级15转为二进制数值为0b1111,且NVIC的优先级分组为2,那么前两位为0b11,后两位也是0b11,这两个数值的意义等同于片上外设中断源的抢占优先级和子优先级,因此可见,SysTick的抢占优先级和子优先级都低于片上片上外设某个中断源优先级和子优先级。如果当两个的软件优先级都配置成一致,那么就比较它们在中断向量表中的硬件编号,编号越小优先级越高。


5. SysTick系统定时器编程实践


阐述了以上知识点,下来就是编程练习了:利用SysTick系统定时器,实现精准延时,这个在实际工程项目中十分常用。 


硬件平台正点原子MiniSTM32,实现的功能是精准延时让板载的两个LED灯闪烁。编程的要点是调用SysTick_Config()函数设置计数器的重装载值。 


新建文件main.c和systick_test.h分别用于实现主体功能和函数声明:


systick_test.h

#ifndef __SYSTICK_TEST_H__

#define __SYSTICK_TEST_H__


#include "stm32f10x_conf.h"


void Led_CfgInit(void);

void SysTick_Delay_Us(__IO uint32_t us);

void SysTick_Delay_Ms(__IO uint32_t ms);


#endif /* __SYSTICK_TEST_H__ */


main.c

#define ALL_LED_ON  GPIO_ResetBits(GPIOA,GPIO_Pin_8);\

                    GPIO_ResetBits(GPIOD,GPIO_Pin_2)


#define ALL_LED_OFF GPIO_SetBits(GPIOA,GPIO_Pin_8);\

                    GPIO_SetBits(GPIOD,GPIO_Pin_2)


//PA8-->LED0,PD2-->LED1

void Led_CfgInit(void)

{

    GPIO_InitTypeDef GPIO_InitTypeStu;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);


    GPIO_InitTypeStu.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_8;

    GPIO_InitTypeStu.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitTypeStu);

    GPIO_SetBits(GPIOA,GPIO_Pin_8);         


    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_2;

    GPIO_Init(GPIOD, &GPIO_InitTypeStu);

    GPIO_SetBits(GPIOD,GPIO_Pin_2);         

}


//配置SysTick的计数器计数的时间是1us,每间隔1us,CTRL的BIT[16]由0刷新为1

void SysTick_Delay_Us(__IO uint32_t us)

{

    uint32_t i;


    SysTick_Config(72);


    for (i = 0; i < us; i++)

    {

        while (!((SysTick->CTRL >> 16) & 0x01));

    }

    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

}


//配置SysTick的计数器计数的时间是1ms,每间隔1us,CTRL的BIT[16]由0刷新为1

void SysTick_Delay_Ms(__IO uint32_t ms)

{

    uint32_t i;


    SysTick_Config(72000);


    for (i = 0; i < ms; i++)

    {

        //while (!(SysTick->CTRL & (1 << 16)));

        while (!((SysTick->CTRL >> 16) & 0x01));

    }

    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

}


int main(void)

{

    SysTick_Delay_Us(10);

    Led_CfgInit();


    while (1)

    {

        ALL_LED_ON;


        SysTick_Delay_Ms(1000);


        ALL_LED_OFF;


        SysTick_Delay_Ms(5000);

    }

}


SysTick_Delay_Ms()和SysTick_Delay_Us()分别实现n个1Ms定时和n个1Us定时的功能。 


以SysTick_Delay_Ms()为例,其中的SysTick_Config(72000)定义了每间隔1ms触发一次SysTick中断,当然,同时CTRL的BIT[16]会由0变为1,随后计数器又会从重装载器中取出72000开始递减,此时TRL的BIT[16]会由1变为0,如此循环。基于这个特点,这里采用轮询CTRL的BIT[16]的方法以达到延时的目的,这里中断处理函数未使用。


下来采用中断处理函数的方法:


#ifndef __SYSTICK_TEST_H__

#define __SYSTICK_TEST_H__


#include "stm32f10x_conf.h"


void Led_CfgInit(void);

void SysTick_CfgInit(void);

void SysTick_Delay(__IO uint32_t ms);


#endif /* __SYSTICK_TEST_H__ */


main.c

extern uint32_t SystemCoreClock;

uint32_t IT_Cnt;


#define ALL_LED_ON  GPIO_ResetBits(GPIOA,GPIO_Pin_8);\

                    GPIO_ResetBits(GPIOD,GPIO_Pin_2)


#define ALL_LED_OFF GPIO_SetBits(GPIOA,GPIO_Pin_8);\

                    GPIO_SetBits(GPIOD,GPIO_Pin_2)


//PA8-->LED0,PD2-->LED1

void Led_CfgInit(void)

{

    GPIO_InitTypeDef GPIO_InitTypeStu;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);


    GPIO_InitTypeStu.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_8;

    GPIO_InitTypeStu.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitTypeStu);

    GPIO_SetBits(GPIOA,GPIO_Pin_8);         



    GPIO_InitTypeStu.GPIO_Pin = GPIO_Pin_2;

    GPIO_Init(GPIOD, &GPIO_InitTypeStu);

    GPIO_SetBits(GPIOD,GPIO_Pin_2);         

}


void StickTick_CfgInit()

{

    SysTick_Config(SystemCoreClock / 1000);

}


void SysTick_Delay(__IO uint32_t ms)

{

    IT_Cnt = ms;

    while (IT_Cnt != 0);

}


int main(void)

{

    StickTick_CfgInit();    //1ms产生一次SysTick中断  

    while (1)

    {


        ALL_LED_ON;

        SysTick_Delay(1000);

        ALL_LED_OFF;

        SysTick_Delay(1000);

    }


    return 0;


}



代码实现的功能是:SysTick系统定时器每1ms产生一次SysTick中断,在中断处理函数中,


void SysTick_Handler(void)

{

    IT_Cnt--;

}


只是简单的对毫秒总数递减,SysTick_Delay()阻塞在IT_Cnt != 0,超时后IT_Cnt为0,延时函数退出。逻辑都比较简单,不赘述。 


这里要提的是,SysTick系统定时器超时时会产生中断,也就是说它是属于STM32中断体系中的,而且相关寄存器内嵌在NVIC中,那为什么不对NVIC_InitTypeDef描述结构体进行初始化呢?这个问题的答案可以在前面的SysTick系统定时器中断优先级小节得出答案,我们从另一个特点看,NVIC_InitTypeDef的结构体原型如下:


typedef struct

{

  uint8_t NVIC_IRQChannel;                    

  uint8_t NVIC_IRQChannelPreemptionPriority;  

  uint8_t NVIC_IRQChannelSubPriority;         

  FunctionalState NVIC_IRQChannelCmd;          

} NVIC_InitTypeDef;


以成员NVIC_IRQChannel为例,它是指定中断/事件的通道的,为uint8_t类型,但是SysTick_IRQn等于-1,显然与之类型不匹配,加上前面我们讲到,Systick属于内核外设,跟普通外设的中断优先级存在差别,普通外设的中断/事件才需要设置NVIC,所以咧,它并不需要初始化NVIC_InitTypeDef,SysTick相关的初始化操作已经在SysTick_Config()函数中实现了。


系统定时器SysTick其实也不难,STM32支持跑OS,所以SysTick存在最大的意义就是为OS提供时基单元,多个时基单元可以组成一个时间片,时间片就是OS任务线程执行的轮回。学习的重点在于计数值的设置、以及理解与外部中断的差别。先总结到这,下一篇讲通用定时器。

关键字:STM32  系统定时器  SysTick 引用地址:STM32系统定时器SysTick

上一篇:STM32初识——中断初始化过程(by woody)
下一篇:关于中断嵌套中的SysTick中断

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

基于stm32串口环形缓冲队列处理机制—入门级(单字节)
1.1 实验简介 最简单的串口数据处理机制是数据接收并原样回发的机制是:成功接收到一个数,触发进入中断,在中断函数中将数据读取出来,然后立即。这一种数据处理机制是“非缓冲中断方式”,虽然这种数据处理方式不消耗时间,但是这种数据处理方式严重的缺点是:数据无缓冲区,如果先前接收的的数据如果尚未发送完成(处理完成),然后串口又接收到新的数据,新接收的数据就会把尚未处理的数据覆盖,从而导致“数据丢包”。 对于“数据丢包”,最简单的办法就是使用一个数组来接收数据:每接收一个数据,数组下标偏移。虽然这样的做法能起到一定的“缓冲效果”,但是数组的空间得不到很好的利用,已处理的数据仍然会占据原有的数据空间,直到该数组“满载”(数组
[单片机]
基于<font color='red'>stm32</font>串口环形缓冲队列处理机制—入门级(单字节)
STM32 USB的DFU功能
最近用到USB,后来发现了STM32F103可以程序实现DFU,在网上搜到代码对比官方文件发现是大容量FLASH的DFU,我用的是STM32F103C8T6,属于中容量。自己对着官方例程修改了网友的代码,改成中容量后可以使用DFU更新固件。修改过程中要注意几点 1.在库函数“void SystemInit (void)”结尾处添加向量表偏移量,开全局中断(在dfu中关闭了中断,在此处打开)。如下图 2.修改MDK app程序偏移量,如下图:(预留了20K FLASH给USB DFU使用,所以APP代码起始地址0x8005000) 3.APP修改完成后,需要在DFU代码中修改APP跳转地址,如下图: 修改完成后,使用
[单片机]
<font color='red'>STM32</font> USB的DFU功能
STM32以太网程序解析二
--------------------------------------------------------------------------------------------------------------------------- 下面我们来详细看一下程序,我们将逐行的进行分析。 1. int simple_server(void) 2. { 3. unsigned int plen,dat_p,i1=0,payloadlen=0; 4. unsigned char i=0,*buf1 = 0; 5. signed char cmd; 6. 7. /
[单片机]
STM32 数据类型定义
在 Keil MDK 开发环境中,经常会遇到类似于unsigned int 8、uint8_t 、u8等数据变量定义,对于初学者来讲确实是有点痛苦,后来查询发现以上三种方式均表示——无符号的8位整形数据。 由于C语言类型的长度完全由编译器决定,char 通常被定义成 8 位宽;int 通常被定义成 16 位或 32 位宽(或更高),它取决于平台(编译器将在这两者间选择最合适的字宽);short 通常被定义成 16 位宽;long 通常被定义成 32 或 64位宽。 所以 C99 中引进了一个标准C库头文件 stdint.h ,方便精确确定整数类型的宽度 定义标准的扩展整数类型_stdint.h文件(部分) /* Sig
[单片机]
<font color='red'>STM32</font> 数据类型定义
STM32的GPIO—快速IO的使用与讲解
STM32的每个GPIO端口都有两个特别的寄存器,GPIOx_BSRR和GPIOx_BRR寄存器,通过这两个寄存器可以直接对对应的GPIOx端口置'1'或置'0'。 GPIOx_BSRR的高16位中每一位对应端口x的每个位,对高16位中的某位置'1'则端口x的对应位被清'0';寄存器中的位置'0',则对它对应的位不起作用。 GPIOx_BSRR的低16位中每一位也对应端口x的每个位,对低16位中的某位置'1'则它对应的端口位被置'1';寄存器中的位置'0',则对它对应的端口不起作用。 简单地说
[单片机]
STM32 系统架构及存储器映射
一、STM32系统架构 STM32系统架构如下图所示: 主系统有以下部分构成: DCode总线 该总线将M3内核的DCode总线与闪存存储器数据接口相连 ICode总线 该总线将M3内核的ICode总线与闪存存储器指令接口相连,指令取指在该总线上完成 系统总线S-bus 此总线连接Cortex™-M3内核的系统总线(外设总线)到总线矩阵,总线矩阵协调着内核和DMA间的访问。 DMA总线 DMA1和DMA2 此总线将DMA的AHB主控接口与总线矩阵相联,总线矩阵协调着CPU的DCode和DMA到SRAM、闪存和外设的访问。 总线矩阵 此总线矩阵协调内核系统总线和DMA主控总线之间的访问仲裁。此仲裁利用轮换算法。此总线矩阵
[单片机]
stm32 io模拟spi通信
首先借鉴他人的编写程序: #define MOSI_H GPIO_SetBits(GPIOB, GPIO_Pin_10) #define MOSI_L GPIO_ResetBits(GPIOB, GPIO_Pin_10) #define SCLK_H GPIO_SetBits(GPIOB, GPIO_Pin_13) #define SCLK_L GPIO_ResetBits(GPIOB, GPIO_Pin_13) #define MISO GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) unsigned char SPI_SendByte(unsigned char dt)
[单片机]
stm32入门——跑马灯(基于stm32f103zet6)
最近开始学stm32,着实感觉到了stm32和51之间的区别,但也有联系,总我感觉32与51之间最大的区别就是在使用某个外设之前,要对该外设进行时钟的使能(以达到降低功耗的目的),和相关配置。 刚学完跑马灯,下面对跑马灯用到的对IO口的配置相关知识分别对应官方库函数和寄存器进行总结。 如有错误或不足,请在下方留言。 文章内容基于正点原子战舰。 IO口的状态 IO口有八大模式:─ 输入浮空( GPIO_Mode_IN_FLOATING = 0x04,) ─ 输入上拉( GPIO_Mode_IPU = 0x48,) ─ 输入下拉( GPIO_Mode_IPD
[单片机]
<font color='red'>stm32</font>入门——跑马灯(基于stm32f103zet6)
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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