返回列表 发新帖我要提问本帖赏金: 151.00元(功能说明)

[应用相关] 歪解单片机的时钟系统--关于内外时钟切换及时钟超频测试

[复制链接]
4128|13
 楼主| 呐咯密密 发表于 2021-11-29 14:09 | 显示全部楼层 |阅读模式
本帖最后由 呐咯密密 于 2021-12-13 15:11 编辑

[url=home.php?mod=space&uid=760190]@21小跑堂 #申请原创#[/url]
前言
距离上次的原创帖的发布又过了很久时间没更新了,作为国家一级健忘运动员的@21小跑堂 ,愣是未忘记催更,月底还不忘给家人送温暖(催更!!!)。奈何苦于无文可写,又拖了两天。赶巧这今日有群友困于STM32的时钟系统,这里就小酌一杯,聊表歉意。
9036261a431bf440e0.png
诚然,当使用固件库时,把外部晶振摘掉,系统确实会自动切换到内部时钟,但是只会以8M的默认值运行,显然这是十分不可行的,8M的速度直接让我们的STM32病入膏肓,今天的任务就是让STM32失去外挂(晶振)时,依旧可以激情澎湃。
时钟详解这里不过多介绍,自己也没有别人介绍的好。此帖旨在解决现实问题。
此处插播广告:
       群友问过这种问题,外部接8M晶振和16M晶振有啥区别?
       以我微薄的经验来看,这两个在用的时候差别不大,如果使用ST的固件库(以STM32F103为例),使用8M的晶振会更方便,不用改任何代码,时钟就是72M的全速运行状态。如果用16M晶振,则需要修改代码:
stm32f10x.h中修改宏定义HSE_VALUE    ((uint32_t)8000000)为HSE_VALUE    ((uint32_t)16000000)。
5536461a435a7104bd.png
之后进入system_stm32f10x.c,将RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);改为RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9);此处是将输入时钟二分频为8M,再进行9倍频到72M,和使用了8M没区别。如果不进行该二分频操作,时钟还是有的,但是会以16M为基准进行9倍频到144M,此时单片机以超频模式运行,也是可以运行的。但是时钟的精准性不能得到保证。
9866761b6f1f1cd036.png
系统的时钟可以通过添加代码在debug模式下显示:
  1. RCC_ClocksTypeDef ClockInfo;
  2. RCC_GetClocksFreq(&ClockInfo);
3565661a439c49ffe1.png
通过debug模式下观察ClockInfo的值便可知道此时系统时钟速度:
3277661a43a491f023.png
这里提一下,在使用外部晶振的情况下,ST即使是超频,依旧发挥稳定,不得不夸一下ST的质量。
此时我将我的开发板以8M的基准倍频16倍,得到128M的主频,使用定时器定时10us,示波器测试无误差。串口通信无误。
7058961a4405c14544.png

1273161a44046e7202.png

4517961a4407143bc1.png
9574561a464e157765.png
以72M的主频跑128依旧稳定,赞一个,因为我的外部晶振只有8M最大只能倍频到128,如果使用外部16M,不知继续倍频可以到多少。不过性能还是很好的。
*******************************************
预留测试GD32的效果:
写贴时将GD的GD32E230翻出来进行了同样的测试,因为GD的倍频器倍数较高,我已经倍频到144M(标准72M),测试定时器依旧稳定。
3156461a459d438c03.png
*******************************************
广告很长,你忍一下。
上半场结束,下半场继续:
此处歪解一下时钟的问题,之前有群友很疑惑单片机的低功耗和时钟的关系,疑惑高速的时钟会不会增加MCU的功耗,为啥低功耗要降低时钟速度。这里讲解一下:
可以用用单位时间内执行的指令来看,高速时钟在单位时间内使系统跑了更多的指令,而低速时钟单位时间内跑的少,而单片机是直线结构,内核是不会休息的,功耗就看执行的指令多少。而单片机的低功耗就是降低时钟,让单片机跑慢点。就像人一样,低功耗相当于你不跑了,原地休息,但是你的心跳不会停止,你还是得消耗能量,即使再少还得消耗。
就像人一样,时钟就相当于心跳,只要还活着就得消耗能量,你要想跑得快,心脏就得跳得快,跳得越快能量消耗越高,即使你去睡觉,心跳只要不停止,你还得消耗能量,如果心跳没了,整个人就没了,MCU也就宕机了。所以在处理低功耗时最先解决的就是时钟频率,只有降低了时钟的频率,才能真正降低功耗。关于单片机进入低功耗和唤醒,以及降低整体运行功耗我看能不能在下文讲解,近期刚好做了一个低功耗的项目,这里留悬念吧。

/********************************************************************************************************/
广告结束,正文开始,不好意思,有点喧宾夺主了哈。
回到主题,为了解决时钟切换的问题,才有了这个帖子,上文全属歪楼,为最近开发时的经验总结。
我们在使用STM32103的固件库时,时钟配置在system_stm32f10x.c中,但是只是对外部晶振做了初始化,而对于内部时钟并没有添加代码,如果你的MCU没有外部晶振,当系统运行时是先启动内部时钟,然后会检测外部晶振,如果没有检测到晶振,系统便以内部的8M继续运行,这是不合理的。
1236261a445b8b099e.png
这里可以看到,如果外部启动失败,会进入这个else,但是这个else中并未添加任何代码,所以只会用8M的内钟执行,我们要做的就是在else中添加外部启动失败的代码:
  1.     /* 开启HSI 即内部晶振时钟 */
  2.         RCC->CR |= (uint32_t)0x00000001;

  3.         /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/
  4.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2;

  5.                         
  6.         /*PLLCLK=8/2*13=52MHz   设置倍频得到时钟源PLL的频率*/
  7.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL12;

  8.         /* PLL不分频输出  */
  9.         RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  10.          
  11.         /* 使能 PLL时钟 */
  12.         RCC->CR |= RCC_CR_PLLON;

  13.         /* 等待PLL时钟就绪*/
  14.         while((RCC->CR & RCC_CR_PLLRDY) == 0)
  15.         {
  16.         }


  17.         /* 选择PLL为系统时钟的时钟源 */
  18.         RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  19.         RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   

  20.         /* 等到PLL成为系统时钟的时钟源*/
  21.         while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  22.         {

  23.         }
该代码填充后如果检测到有外部时钟,便以外部时钟为基准进行时钟的倍频处理,达到用户想要的时钟频率,如果你的MCU没有外部时钟,则会执行else内部的代码,将时钟源切换到内部时钟并进行倍频。如此便达到了自动检测时钟的目的。问题:这是我根据STM32F031的时钟切换代码演变来的,但是这个只能用于主频小于或等于48M时使用,如果倍频因子超过12,也就是主频超过48M是,就会出现硬件错误,直接卡死。当需要更高的主频时就需要如下配置。
在else里面最开头添加:
  1.     /* Enable Prefetch Buffer */
  2.     FLASH->ACR |= FLASH_ACR_PRFTBE;
  3.     /* Flash 2 wait state */
  4.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  5.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;        
4089261a468f7ccb97.png

问:
如果我的MCU有晶振,但是我不想用外部,就想用内部,如何处理呢?
答:
打一顿就好了,有外部不用干啥用内部呢?
上述纯属恶搞自己,被坑过。。。
因为内部时钟不准!!!测试内部时钟在使用定时器时会有偏差,本人在此吃过亏此问题在STM32F031和GD32E230中均有体现。但是USART和SPI通信是正常的,即使我用的2.5M波特率的USART和8M的SPI。
解决办法,上述代码不用动,添加如下代码。
5195661a4486e07eae.png
通过注释原文RCC->CR |= ((uint32_t)RCC_CR_HSEON);并添加RCC->CR &= ~((uint32_t)RCC_CR_HSEON);可默认之以内部时钟方式启动。
注意在主函数加上SystemInit();函数哦!!!
最终代码如下:
  1. static void SetSysClockTo72(void)
  2. {
  3.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  4.   
  5.   /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/   
  6.   /* Enable HSE */   
  7. //  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  8.         /*取消改行注释并注释上文,可默认启动内部时钟*/
  9.         RCC->CR &= ~((uint32_t)RCC_CR_HSEON);
  10.   /* Wait till HSE is ready and if Time out is reached exit */
  11.   do
  12.   {
  13.     HSEStatus = RCC->CR & RCC_CR_HSERDY;
  14.     StartUpCounter++;  
  15.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  16.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  17.   {
  18.     HSEStatus = (uint32_t)0x01;
  19.   }
  20.   else
  21.   {
  22.     HSEStatus = (uint32_t)0x00;
  23.   }  

  24.   if (HSEStatus == (uint32_t)0x01)
  25.   {
  26.     /* Enable Prefetch Buffer */
  27.     FLASH->ACR |= FLASH_ACR_PRFTBE;

  28.     /* Flash 2 wait state */
  29.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  30.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;   


  31.     /* HCLK = SYSCLK */
  32.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  33.       
  34.     /* PCLK2 = HCLK */
  35.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  36.    
  37.     /* PCLK1 = HCLK */
  38.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

  39. #ifdef STM32F10X_CL
  40.     /* Configure PLLs ------------------------------------------------------*/
  41.     /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
  42.     /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
  43.         
  44.     RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
  45.                               RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
  46.     RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
  47.                              RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
  48.   
  49.     /* Enable PLL2 */
  50.     RCC->CR |= RCC_CR_PLL2ON;
  51.     /* Wait till PLL2 is ready */
  52.     while((RCC->CR & RCC_CR_PLL2RDY) == 0)
  53.     {
  54.     }
  55.    
  56.    
  57.     /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
  58.     RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
  59.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
  60.                             RCC_CFGR_PLLMULL9);
  61. #else   
  62.     /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
  63.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
  64.                                         RCC_CFGR_PLLMULL));
  65.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL16);
  66. #endif /* STM32F10X_CL */

  67.     /* Enable PLL */
  68.     RCC->CR |= RCC_CR_PLLON;

  69.     /* Wait till PLL is ready */
  70.     while((RCC->CR & RCC_CR_PLLRDY) == 0)
  71.     {
  72.     }
  73.    
  74.     /* Select PLL as system clock source */
  75.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  76.     RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   

  77.     /* Wait till PLL is used as system clock source */
  78.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  79.     {
  80.     }
  81.   }
  82.   else
  83.   {
  84.     /* Enable Prefetch Buffer */
  85.     FLASH->ACR |= FLASH_ACR_PRFTBE;
  86.     /* Flash 2 wait state */
  87.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  88.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;         
  89.            /* 开启HSI 即内部晶振时钟 */
  90.         RCC->CR |= (uint32_t)0x00000001;

  91.         /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/
  92.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2;

  93.                         
  94.         /*PLLCLK=8/2*13=52MHz   设置倍频得到时钟源PLL的频率*/
  95.         RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL16;

  96.         /* PLL不分频输出  */
  97.         RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  98.          
  99.         /* 使能 PLL时钟 */
  100.         RCC->CR |= RCC_CR_PLLON;

  101.         /* 等待PLL时钟就绪*/
  102.         while((RCC->CR & RCC_CR_PLLRDY) == 0)
  103.         {
  104.         }


  105.         /* 选择PLL为系统时钟的时钟源 */
  106.         RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  107.         RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   

  108.         /* 等到PLL成为系统时钟的时钟源*/
  109.         while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
  110.         {

  111.         }
  112.   }
  113. }

在STM32F030或者STM32F031中同样可以做类似操作:
  1. static void SetSysClock(void)
  2. {
  3.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  4.   
  5.   /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/
  6.   /* Enable HSE */   

  7. RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  8.         //修改为内部晶振        
  9. //        RCC->CR &= ~((uint32_t)RCC_CR_HSEON);

  10.   /* Wait till HSE is ready and if Time out is reached exit */
  11.   do
  12.   {
  13.     HSEStatus = RCC->CR & RCC_CR_HSERDY;
  14.     StartUpCounter++;  
  15.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  16.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  17.   {
  18.     HSEStatus = (uint32_t)0x01;
  19.   }
  20.   else
  21.   {
  22.     HSEStatus = (uint32_t)0x00;
  23.   }  

  24.   if (HSEStatus == (uint32_t)0x01)
  25.   {
  26.     /* Enable Prefetch Buffer and set Flash Latency */
  27.     FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;

  28.     /* HCLK = SYSCLK */
  29.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  30.       
  31.     /* PCLK = HCLK */
  32.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;

  33.     /* PLL configuration = HSE * 6 = 48 MHz */
  34.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
  35.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL7);
  36.             
  37.     /* Enable PLL */
  38.     RCC->CR |= RCC_CR_PLLON;

  39.     /* Wait till PLL is ready */
  40.     while((RCC->CR & RCC_CR_PLLRDY) == 0)
  41.     {
  42.     }

  43.     /* Select PLL as system clock source */
  44.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  45.     RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   

  46.     /* Wait till PLL is used as system clock source */
  47.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
  48.     {
  49.     }
  50.   }
  51.   else
  52.   { /* If HSE fails to start-up, the application will have wrong clock
  53.          configuration. User can add here some code to deal with this error */
  54.                    // HSI 内部时钟做为PLL时钟源并配置PLL 56M做为系统时钟
  55.     /* Enable Prefetch Buffer and set Flash Latency */
  56.     FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;

  57.     /* HCLK = SYSCLK */
  58.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

  59.     /* PCLK = HCLK */
  60.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;

  61.     // PLL configuration = (HSI/2) * 12 = 48 MHz
  62.     RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_14); // 8M/2 * 14 = 56M

  63.     /* Enable PLL */
  64.     RCC->CR |= RCC_CR_PLLON;

  65.     /* Wait till PLL is ready */
  66.     while ((RCC->CR & RCC_CR_PLLRDY) == 0)
  67.     {
  68.     }

  69.     /* Select PLL as system clock source */
  70.     RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // PLL 做系统时钟

  71.     /* Wait till PLL is used as system clock source */
  72.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
  73.     {
  74.     }
  75.   }  
  76. }
在STM32F103中,使用内部晶振,最大时钟频率也只能到64M,受倍频因子的影响嘛,最大只能倍频16倍。  但是在STM32F031中,标准使用内部时钟主频只有48M,但是我们仍然可以继续倍频,用内部时钟进行超频达到64M。在我们的产品中就用过内部超频到56M,USART和SPI长时间无问题。
而GD32E230因为其高达32的倍频因子,内部时钟可以倍频到128M。
2560961a46dec2b220.png
但是这种几分钟内没有明显发热现象,不敢做长时间测试,现在MCU有点小贵。干费一个就心疼。
总之,无论ST还是国产,其主频更适合在规定的范围内运行,但是跑极限在短时间内也没有很大的问题。这些数据仅供参考。
至此单片机时钟讲解就结束了,没有多少理论性的东西,主要是解决一些时钟使用时的问题,自己也总是忘,留帖一篇作为自省。
此帖中所有代码都经过本人测试,运行无任何问题,但是对于问题的阐述或者一些见解可能有错误,欢迎大佬们批评指正,一定接受各种批评,努力完善。

打赏榜单

qqaa123 打赏了 1.00 元 2021-12-02
理由:看着很牛逼的样子

21小跑堂 打赏了 150.00 元 2021-12-01
理由:恭喜通过原创奖文章审核!请多多加油哦!

yangjiaxu 发表于 2021-12-1 14:39 | 显示全部楼层
整挺好,其实ST的时钟还是挺简单的,可以用cubemx配置
Allison8859 发表于 2021-12-1 14:41 | 显示全部楼层
如果用12M外置的晶振,是不是要改倍频的参数啊?
Annie556 发表于 2021-12-1 14:42 | 显示全部楼层
我之前遇到一个用6M的STM32F103,为啥要用这个大小的晶振呢??
 楼主| 呐咯密密 发表于 2021-12-1 14:43 | 显示全部楼层
Allison8859 发表于 2021-12-1 14:41
如果用12M外置的晶振,是不是要改倍频的参数啊?

是的,假如是12M的外置,可以修改倍频系数为6,就可以到72M
 楼主| 呐咯密密 发表于 2021-12-1 14:44 | 显示全部楼层
yangjiaxu 发表于 2021-12-1 14:39
整挺好,其实ST的时钟还是挺简单的,可以用cubemx配置

整挺好,下次不许说了,(这不是库不一样嘛)

评论

他手上的可能是祖传代码  发表于 2021-12-7 16:33
 楼主| 呐咯密密 发表于 2021-12-1 14:46 | 显示全部楼层
Annie556 发表于 2021-12-1 14:42
我之前遇到一个用6M的STM32F103,为啥要用这个大小的晶振呢??

用的晶振大小其实无所谓的,在规定范围内就行,修改倍频系数就好,至于为啥用,可能人家手里只有6M的,或者6M的库存多,或者价格便宜,或者好拿货。
asmine 发表于 2021-12-2 17:14 | 显示全部楼层
楼主你好,
问一下
所谓的超频使用,是指配置不变,把外部晶振换大一点么?
这里没太理解,

一般用的时候,就是配置成推荐的最大时钟,没玩过这么大的。
 楼主| 呐咯密密 发表于 2021-12-2 17:23 | 显示全部楼层
asmine 发表于 2021-12-2 17:14
楼主你好,
问一下
所谓的超频使用,是指配置不变,把外部晶振换大一点么?

超频就是让单片机的主频超过规定的合理范围,如果你不修改配置,直接换大晶振,可能不行,至少要去修改一下外部晶振大小的宏定义,修改完其他不动也能达到超频。如果不改晶振,就去改代码。最终的目的就是将主频变大.
当然,一般的应用是不会超频的,但是不绝对,我在用STM32F031就是把48M超频到56,达到更快的处理速度,目前产品无任何问题,这样做就是为了省钱.
asmine 发表于 2021-12-2 17:27 | 显示全部楼层
啊,我上面表述漏了,把改晶振的宏定义给丢了。

一直看网上说单片机超频使用,琢磨着怎么超频呢?弄的很神秘的样子,
原来这样就可以,或者试着把各个系数改一改应该也行呗?

感谢解惑
 楼主| 呐咯密密 发表于 2021-12-2 17:32 | 显示全部楼层
asmine 发表于 2021-12-2 17:27
啊,我上面表述漏了,把改晶振的宏定义给丢了。

一直看网上说单片机超频使用,琢磨着怎么超频呢?弄的很神 ...

对,不改硬件,修改倍频系数就能实现
caigang13 发表于 2021-12-2 21:12 来自手机 | 显示全部楼层
歪解,呵呵了。
hi0712 发表于 2021-12-3 14:25 | 显示全部楼层
哈哈哈
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

568

主题

4085

帖子

56

粉丝
快速回复 在线客服 返回列表 返回顶部