你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

在MDK-ARM5.17对STM32的USART1进行软件仿真时串口界面收不到数据

[复制链接]
吐息间丶时光中 发布时间:2017-2-14 16:34
本帖最后由 吐息间丶时光中 于 2017-2-21 00:01 编辑

小白是STM32初学者,遇到的一些问题在大神们面前太小儿科,还请大神不吝赐教

【问题一:TE置位自动发送一个空闲帧】
    前几天看刘凯老师的STM32培训视频第18集学习USART这一模块,在MDK-ARM5.17上进行软件仿真过程遇到一个坎硬是过不去:按照参考手册配置完寄存器后,软件仿真调试时(无论是单步一条一条的运行程序,还是全速运行程序),TXE一直处于低电平(
也就导致串口窗口收不到数据)
    代码如下(与刘凯老师视频上的一致):

  1. #include"stm32f10x.h"

  2. #define RCC_APB2ENR_Addr        (RCC_BASE + 0x18)

  3. #define BitBind(Addr,BitNum)        *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
  4.                                                                 ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))

  5. #define RCC_APB2ENR(n)        BitBind(RCC_APB2ENR_Addr,n)

  6. /**********************************************************************
  7. * Function Name : MAIN
  8. * Description         : main program
  9. * Input                 : None
  10. * Output                 : None
  11. * Return                 : None
  12. **********************************************************************/
  13. int main(void)
  14. {/***UART寄存器方式编程***/
  15.         uint32_t BAUD=9600;                                //波特率9600bps
  16.         uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00;        //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
  17.         float USARTDIV=0.0;                                //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
  18.         
  19.         //寄存器配置:USART使能、字长、停止位、波特率、发送使能
  20.         RCC_APB2ENR(14)=1;                                //USART1时钟使能
  21. //        RCC_APB2ENR(2)=1;                                //GPIOA时钟使能(软件仿真不开启无影响)
  22. //        GPIOA->CRH |= (3<<4);                        //软件仿真选择通用推挽输出也有效果(但不建议)
  23.         GPIOA->CRH |= (11<<4);                        //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
  24.         GPIOA->CRH &= ~(1<<6);                        //PA9复用功能推挽输出(最大速度50MHz)
  25.         
  26.         USART1->CR1 |= (1<<13);                        //USART使能
  27.         USART1->CR1 &= ~(1<<12);                //1个起始位、8个数据位
  28.         USART1->CR1 &= ~(1<<10);                //禁止校验
  29.         USART1->CR2 &= ~(7<<11);                //1个停止位、禁止CK引脚(即异步)
  30.         //波特率算法
  31.         USARTDIV = (72000000)/(16.0*BAUD);                                //注:整数除法会截掉右边的小数部分
  32.         DIV_Mantissa = USARTDIV;                                                //取整
  33.         DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5;        //小数部分四舍五入并取整
  34.         USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction;        //也即:USART1->BRR = 0x1D4C;               

  35.         USART1->CR1 |= (1<<3);                        //发送使能
  36.         USART1->DR  = 0x66;                                //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
  37.         
  38.         return 0;
  39. }
复制代码
调试界面:
1.png

    起初以为是寄存器配置出错,所以把参考手册相关章节又看了一遍,后来发现并没有配置失误,于是又去查看了固件库里的USART配置函数,发现与自己的配置确实无异,就这样来来去去地鼓捣了两天……后来,在某一次调试时,小白将那全速运行按钮点了两次,奇迹出现——数据显示出来了!
    那么问题是出现在这吗——因为刘凯老师视频里用的Keil版本是Ver4,而我的是Ver5,所以不同?而且网上也有很多吐槽Ver5有很多bug。怀着这种心态,刚开始那会小白也相信是这样。但小白的性格有点追求小小的完美,于是对自己刚发现的持半信半疑的态度,打算再看一看参考手册……或许是功夫不负有心人吧(实际是自己总是看视频,动脑太少),最终总算有了一个较满意的发现:将上面代码中的以下两句

  1. USART1->CR1 |= (1<<3);                        //发送使能
  2.         USART1->DR  = 0x66;                                //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
复制代码
进行如下更改
  1. USART1->CR1 |= (1<<3);                                                        //发送使能
  2.         while((USART1->SR & 0x40) != 0x40);                                //置位TE将使得USART在第一个数据帧前发送一空闲帧
  3.         USART1->DR  = 0x66;                                //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
  4.         while((USART1->SR & 0x40) != 0x40);
  5.         USART1->CR1 &= ~(1<<3);                        //发送失能
复制代码
   经过以上更改后,再次在MDK-ARM5.17里进行软件仿真时,仅点击一次全速运行即可完成任务。如果是点击单步运行,仅在语句“while((USART1->SR & 0x40) != 0x40);”处单步一次无法单步到下一条语句(应该是MDK仿真发送完一个数据设定有一定的时间段),此处可以将光标定位在下一条语句后点击调试按钮“Run to Cursor Line”(Ctrl+F10)即可。

更正后的完整代码如下:

  1. #include"stm32f10x.h"

  2. #define RCC_APB2ENR_Addr        (RCC_BASE + 0x18)

  3. #define BitBind(Addr,BitNum)        *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
  4.                                                                 ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))

  5. #define RCC_APB2ENR(n)        BitBind(RCC_APB2ENR_Addr,n)

  6. /**********************************************************************
  7. * Function Name : MAIN
  8. * Description         : main program
  9. * Input                 : None
  10. * Output                 : None
  11. * Return                 : None
  12. **********************************************************************/
  13. int main(void)
  14. {/***UART寄存器方式编程***/
  15.         uint32_t BAUD=9600;                                //波特率9600bps
  16.         uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00;        //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
  17.         float USARTDIV=0.0;                                //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
  18.         
  19.         //寄存器配置:USART使能、字长、停止位、波特率、发送使能
  20.         RCC_APB2ENR(14)=1;                                //USART1时钟使能
  21. //        RCC_APB2ENR(2)=1;                                //GPIOA时钟使能(软件仿真不开启无影响)
  22. //        GPIOA->CRH |= (3<<4);                        //软件仿真选择通用推挽输出也有效果(但不建议)
  23.         GPIOA->CRH |= (11<<4);                        //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
  24.         GPIOA->CRH &= ~(1<<6);                        //PA9复用功能推挽输出(最大速度50MHz)
  25.         
  26.         USART1->CR1 |= (1<<13);                        //USART使能
  27.         USART1->CR1 &= ~(1<<12);                //1个起始位、8个数据位
  28.         USART1->CR1 &= ~(1<<10);                //禁止校验
  29.         USART1->CR2 &= ~(7<<11);                //1个停止位、禁止CK引脚(即异步)
  30.         //波特率算法
  31.         USARTDIV = (72000000)/(16.0*BAUD);                                //注:整数除法会截掉右边的小数部分
  32.         DIV_Mantissa = USARTDIV;                                                //取整
  33.         DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5;        //小数部分四舍五入并取整
  34.         USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction;        //也即:USART1->BRR = 0x1D4C;               

  35.         USART1->CR1 |= (1<<3);                                                        //发送使能
  36.         while((USART1->SR & 0x40) != 0x40);                                //置位TE将使得USART在第一个数据帧前发送一空闲帧
  37.         USART1->DR  = 0x66;                                //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
  38.         while((USART1->SR & 0x40) != 0x40);
  39.         USART1->CR1 &= ~(1<<3);                        //发送失能
  40.         
  41.         return 0;
  42. }
复制代码
   问题的根源也就是置位TE将使得USART在第一个数据帧前发送一空闲帧(参考手册有说明。截图如下),由于版本不同导致MDK仿真发送完一个数据设定的时间段或许有所区别,所以在MDK-ARM5.17上缺少了以上检测语句就达不到刘凯老师视频上的调试效果。
2.png

    同理,该视频刘凯老师给的第二个带有for语句的例子,也必须加上如上检测语句:
  1. #include"stm32f10x.h"

  2. #define RCC_APB2ENR_Addr        (RCC_BASE + 0x18)

  3. #define BitBind(Addr,BitNum)        *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
  4.                                                                 ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))

  5. #define RCC_APB2ENR(n)        BitBind(RCC_APB2ENR_Addr,n)

  6. /**********************************************************************
  7. * Function Name : MAIN
  8. * Description         : main program
  9. * Input                 : None
  10. * Output                 : None
  11. * Return                 : None
  12. **********************************************************************/
  13. int main(void)
  14. {/***UART寄存器方式编程***/
  15.         uint8_t num=0x00,data=0x00;
  16.         uint32_t BAUD=9600;                                //波特率9600bps
  17.         uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00;        //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
  18.         float USARTDIV=0.0;                                //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
  19.         
  20.         //寄存器配置:USART使能、字长、停止位、波特率、发送使能
  21.         RCC_APB2ENR(14)=1;                                //USART1时钟使能
  22. //        RCC_APB2ENR(2)=1;                                //GPIOA时钟使能(软件仿真不开启无影响)
  23. //        GPIOA->CRH |= (3<<4);                        //软件仿真选择通用推挽输出也有效果(但不建议)
  24.         GPIOA->CRH |= (11<<4);                        //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
  25.         GPIOA->CRH &= ~(1<<6);                        //PA9复用功能推挽输出(最大速度50MHz)
  26.         
  27.         USART1->CR1 |= (1<<13);                        //USART使能
  28.         USART1->CR1 &= ~(1<<12);                //1个起始位、8个数据位
  29.         USART1->CR1 &= ~(1<<10);                //禁止校验
  30.         USART1->CR2 &= ~(7<<11);                //1个停止位、禁止CK引脚(即异步)
  31.         //波特率算法
  32.         USARTDIV = (72000000)/(16.0*BAUD);                                //注:整数除法会截掉右边的小数部分
  33.         DIV_Mantissa = USARTDIV;                                                //取整
  34.         DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5;        //小数部分四舍五入并取整
  35.         USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction;        //也即:USART1->BRR = 0x1D4C;               

  36.         data = '@';
  37.         USART1->CR1 |= (1<<3);                                                        //发送使能
  38.         while((USART1->SR & 0x40) != 0x40);                                //置位TE将使得USART在第一个数据帧前发送一空闲帧
  39.         for( ; num < 64 ; num++)                                                //发送64个数据        40H~7FH(ASCII码表)
  40.         {
  41.                 USART1->DR  = data;
  42.                 while((USART1->SR & 0x40) != 0x40);
  43.                 data++;
  44.         }//跳出for循环时data为0x80
  45.         
  46.         return 0;
  47. }
复制代码
   经过小白测试,如果不加将导致第二个数据“41H”无法发送。
如下图所示:
3.png



【问题二:使用printf函数】
    用fputc重定向stdio库的printf函数后:文件流→串口USART1。使用printf函数将数据输出,如果没有提前进行代码配置(关于如何配置各论坛上有类似贴),或者将下图的“USE MicroLIB”勾选:
1.PNG

将导致:在Keil上进行软件仿真时,点击全速运行至少要点击三次(或者将光标定位在主函数里的某条语句,点击“Run to Cursor Line”也至少要点击三次);若是将程序烧进硬件跑,程序短时间未见能工作(未测试时间过久些是否会工作),串口助手收不到输出的数据。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    问题很简单,但困扰了小白好几天,写下来希望其他初学者遇到相同问题时有所参考。
    32初学之路,还请各位大神大大多多指教。


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
收藏 3 评论7 发布时间:2017-2-14 16:34

举报

7个回答
zoomdy 回答时间:2017-2-14 16:54:41
100块买块开发板,谁知道模拟器有什么幺蛾子在里头。
吐息间丶时光中 回答时间:2017-2-14 21:27:52
这个只是软件仿真,不涉及硬件哈。
吐息间丶时光中 回答时间:2017-2-15 18:54:25
今天尝试用固件库函数编程,最终结果仍证明:在MDK-ARM5.17上,必须要在使能TE位后,加上如下语句
  1. while(!USART_GetFlagStatus(USART1, USART_FLAG_TC));
  2. 等价于寄存器方式编程中的:
  3. while((USART1->SR & 0x40) != 0x40);
复制代码


后面的发送才正常。
队长shiwo 回答时间:2017-2-16 08:48:26
谢谢分享 学习
吐息间丶时光中 回答时间:2017-2-17 11:44:22

初学过程,还请多赐教
j923187522 回答时间:2019-1-21 08:33:19
现在应该很厉害了吧 最近在弄lora通信 有没有弄过?想找人交流一下 其实就是串口通信
j923187522 回答时间:2019-1-21 08:38:52
对了 你这种软件仿真是怎么仿真USART的?我仿真不了的?

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版