STM32F105串口USART3无法发送数据或数据错误问题

关于STM32系列的例程网上有很多,大部分是103系列,使用USART3,而且都是直接抄袭书上3.0的固件库或者转载别人的,所以很少又遇到类似我的问题,由于我用105系列芯片,开发板上只有USART3引出来重映射PD8、PD9,用的3.5的固件库,所以调试起来特别麻烦,没有可以运行的例子参考,网上down了一下都发现有问题,自己摸索半天终于发现了解决方案,觉得自己应该做出一点事情,为来源社会略尽微薄之力,所以决定开始写技术博客,一定要我自己原创的,发现别人一般发现不了问题的,绝大多数人转载的那种就算了。


其实103和105/107系列差别很小,只在建立工程的时候要稍微设置一下,有空专门写一篇建工程的,总的来说要使105的USART3要注意几个地方:


1.如果没法收发数据,通常情况是执行printf以后,串口助手只显示空字符串,查看HEX是00,首先要检查硬件,做嵌入式最关键的一点是,在怀疑自己代码之前先看硬件有没有连错,很多时候这样可以省不少事情,像我自己就是刚开始板子上面只连出了RXD和TXD,我就很天真地用3根杜邦线,和9针的电脑串口2、3、5连了,忘了还需要MAX232转电平,调代码搞半天浪费了时间没解决真正的问题,后面找到别的板子的MAX232模块,硬件问题解决。


2.这时候还不能收发数据,查了网上大部分资料,跟预期的一样,都是103的USART1,解决不了我的105的USART3,后面发现有人用USART3出现过类似的问题,常见的代码我就不写出来了,只说发现的关键的地方,USART_Configuration()里面我的代码是:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE); //USART3 remap 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); 
通常例子都会把 RCC_APB2Periph_AFIO忽略掉,在用到“复用重映射”的时候要先使能AFIO的时钟,包括一些淘宝上信誉很好的开发板的所谓例子,我真心怀疑那些人有没有真正调试过,或者调试过3.0版本的,为何不更新3.5的。其次是 GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE)也会有人忽略,重映射必须要这句,最后是开时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE)新手一般会写成APB2,因为是直接COPY过来的,人家用USART1写的APB2当然没错,可是具体用那个USART自己还需要多去琢磨DataSheet到底要怎么设置。


3.这时候数据终于收到了,可是都是乱码,点开HEX,发现每当我发一个byte的数据,它都会显示3个,怎么调试都不对,后面还是发现网上有人同样问题,但是他解决了,最狗日的是他自己说解决了但是不把方案PO出来,你要不回复就算了,反正大家也不知道,既然回复了说自己解决了干嘛不把解决方案放出来,还美滋滋地说自己解决了,太2了这帮人。数据不对多半是波特率的问题,MCU上使用115200波特率,我尝试着降低sscom33串口助手的波特率,发现在38400波特率可以接受到正常数据,印证了网上有个人的说法,用3.5的库设置USART3会出错,直接操作寄存器就好了,有的人说降低波特率,不知道有没有人知道为何?难道真的是3.5的库有问题?

这个时侯要记住,当你要质疑别人的时候,自己先Debug一下,免得成了嘲笑历史的人最终被历史所嘲笑,你要相信那些写STM固件库的人大都是国外名牌大学电子工程/计算机毕业的博士,不是蓝翔的屌丝,固件库都是经过很多压力测试的,很少会出大错,最多是注释有点误导的地方。所以我仔细分析了一下,波特率不对,USART3_Configuration()应该不会出现问题,估计是时钟出现了问题,可是RCC_Configuration()我都是按照固件库函数来的怎么会出错呢?我从stm32f10x_rcc.h源头开始找起,发现默认初始化时钟函数SystemInit()里面有有问题,分频设置当然不会出现问题,问题出在时钟选择上,105/107属于互联网产品,Preprocessor Symbols用的是STM32F10X_CL,固件很多地方会针对这个STM32F10X_CL做出条件编译,其中SystemInit()默认选择外部时钟HSE,HSE的设置值HSE_VALUE在stm32f10x.h中宏定义默认是25MHz,我的板子用的是8MHz晶振,当然那里要改,所以我注释了原来,把8MHz加上。

#if !defined  HSE_VALUE
 #ifdef STM32F10X_CL   
//  #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
 #else 
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
 #endif /* STM32F10X_CL */
#endif /* HSE_VALUE */
然后继续调试,发现还是不行,始终不对,然后继续跟踪SystemInit()函数,最后找到最终决定设置时钟频率的地方,stm32f10x_rcc.h的SetSysClockTo72(),默认选择最高72MHz,根据自己项目的需要的时钟是需要72MHz还是56MHz去跟踪到底是改SetSysClockTo72()还是改SetSysClockTo56(),在里面的条件编译那里:
#ifdef STM32F10X_CL
#if(0)		//chenrunshe 20141127  modify		
    /* Configure PLLs ------------------------------------------------------*/
    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
        
    RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
                              RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
    RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
                             RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);

#else
	    /* PLL2 configuration: PLL2CLK = (HSE / 2) * 10 = 40 MHz */     //HSE = 8MHz 
    /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */  
    RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | 
                              RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); 
    RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV2 | RCC_CFGR2_PLL2MUL10 | 
                             RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); 
#endif
 即原来默认选择25MHz晶振的用的是上面的PLL分频设置,但是实际项目用了8MHz,就要改相应的寄存器设置,反正最后要实现8MHz*9倍分频=72MHz才是标准的,于是我也条件编译一下#if(0)把原来的注释掉,改自己的,这时候最好写上修改信息,涉及到改固件库的,免得后来的人用你的工程文件出问题可以找到原来你修改过这里。修改完以后基本时钟正确了,再来回答之前那个疑问,其实固件库也没有错,网友说的直接改寄存器不用固件库也没有错,但是不能因为改寄存器对就能判断固件库错,其实大家都没错,是你自己错了而已,狗日天杀的谁不按默认晶振设计板子,非得搞跟人家默认不一样,又不标明出来,无语了。 
  

 
  


你可能感兴趣的:(ARM)