本帖最后由 吐息间丶时光中 于 2017-2-21 00:01 编辑
小白是STM32初学者,遇到的一些问题在大神们面前太小儿科,还请大神不吝赐教 。
【问题一:TE置位自动发送一个空闲帧】
前几天看刘凯老师的STM32培训视频第18集学习USART这一模块,在MDK-ARM5.17上进行软件仿真过程遇到一个坎硬是过不去:按照参考手册配置完寄存器后,软件仿真调试时(无论是单步一条一条的运行程序,还是全速运行程序),TXE一直处于低电平(也就导致串口窗口收不到数据)。
代码如下(与刘凯老师视频上的一致):
- #include"stm32f10x.h"
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
- /**********************************************************************
- * Function Name : MAIN
- * Description : main program
- * Input : None
- * Output : None
- * Return : None
- **********************************************************************/
- int main(void)
- {/***UART寄存器方式编程***/
- uint32_t BAUD=9600; //波特率9600bps
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
-
- //寄存器配置:USART使能、字长、停止位、波特率、发送使能
- RCC_APB2ENR(14)=1; //USART1时钟使能
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
- GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
-
- USART1->CR1 |= (1<<13); //USART使能
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
- USART1->CR1 &= ~(1<<10); //禁止校验
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
- //波特率算法
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分
- DIV_Mantissa = USARTDIV; //取整
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
- USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C;
- USART1->CR1 |= (1<<3); //发送使能
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
-
- return 0;
- }
复制代码 调试界面:
起初以为是寄存器配置出错,所以把参考手册相关章节又看了一遍,后来发现并没有配置失误,于是又去查看了固件库里的USART配置函数,发现与自己的配置确实无异,就这样来来去去地鼓捣了两天……后来,在某一次调试时,小白将那全速运行按钮点了两次,奇迹出现——数据显示出来了!
那么问题是出现在这吗——因为刘凯老师视频里用的Keil版本是Ver4,而我的是Ver5,所以不同?而且网上也有很多吐槽Ver5有很多bug。怀着这种心态,刚开始那会小白也相信是这样。但小白的性格有点追求小小的完美,于是对自己刚发现的持半信半疑的态度,打算再看一看参考手册……或许是功夫不负有心人吧(实际是自己总是看视频,动脑太少 ),最终总算有了一个较满意的发现:将上面代码中的以下两句
- USART1->CR1 |= (1<<3); //发送使能
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
复制代码 进行如下更改:
- USART1->CR1 |= (1<<3); //发送使能
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
- while((USART1->SR & 0x40) != 0x40);
- USART1->CR1 &= ~(1<<3); //发送失能
复制代码 经过以上更改后,再次在MDK-ARM5.17里进行软件仿真时,仅点击一次全速运行即可完成任务。如果是点击单步运行,仅在语句“while((USART1->SR & 0x40) != 0x40);”处单步一次无法单步到下一条语句(应该是MDK仿真发送完一个数据设定有一定的时间段),此处可以将光标定位在下一条语句后点击调试按钮“Run to Cursor Line”(Ctrl+F10)即可。
更正后的完整代码如下:
- #include"stm32f10x.h"
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
- /**********************************************************************
- * Function Name : MAIN
- * Description : main program
- * Input : None
- * Output : None
- * Return : None
- **********************************************************************/
- int main(void)
- {/***UART寄存器方式编程***/
- uint32_t BAUD=9600; //波特率9600bps
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
-
- //寄存器配置:USART使能、字长、停止位、波特率、发送使能
- RCC_APB2ENR(14)=1; //USART1时钟使能
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
- GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
-
- USART1->CR1 |= (1<<13); //USART使能
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
- USART1->CR1 &= ~(1<<10); //禁止校验
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
- //波特率算法
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分
- DIV_Mantissa = USARTDIV; //取整
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
- USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C;
- USART1->CR1 |= (1<<3); //发送使能
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
- while((USART1->SR & 0x40) != 0x40);
- USART1->CR1 &= ~(1<<3); //发送失能
-
- return 0;
- }
复制代码 问题的根源也就是置位TE将使得USART在第一个数据帧前发送一空闲帧(参考手册有说明。截图如下),由于版本不同导致MDK仿真发送完一个数据设定的时间段或许有所区别,所以在MDK-ARM5.17上缺少了以上检测语句就达不到刘凯老师视频上的调试效果。
同理,该视频刘凯老师给的第二个带有for语句的例子,也必须加上如上检测语句:
- #include"stm32f10x.h"
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
- /**********************************************************************
- * Function Name : MAIN
- * Description : main program
- * Input : None
- * Output : None
- * Return : None
- **********************************************************************/
- int main(void)
- {/***UART寄存器方式编程***/
- uint8_t num=0x00,data=0x00;
- uint32_t BAUD=9600; //波特率9600bps
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
-
- //寄存器配置:USART使能、字长、停止位、波特率、发送使能
- RCC_APB2ENR(14)=1; //USART1时钟使能
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
- GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
-
- USART1->CR1 |= (1<<13); //USART使能
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
- USART1->CR1 &= ~(1<<10); //禁止校验
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
- //波特率算法
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分
- DIV_Mantissa = USARTDIV; //取整
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
- USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C;
- data = '@';
- USART1->CR1 |= (1<<3); //发送使能
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
- for( ; num < 64 ; num++) //发送64个数据 40H~7FH(ASCII码表)
- {
- USART1->DR = data;
- while((USART1->SR & 0x40) != 0x40);
- data++;
- }//跳出for循环时data为0x80
-
- return 0;
- }
复制代码 经过小白测试,如果不加将导致第二个数据“41H”无法发送。
如下图所示:
【问题二:使用printf函数】
用fputc重定向stdio库的printf函数后:文件流→串口USART1。使用printf函数将数据输出,如果没有提前进行代码配置(关于如何配置各论坛上有类似贴),或者将下图的“USE MicroLIB”勾选:
将导致:在Keil上进行软件仿真时,点击全速运行至少要点击三次(或者将光标定位在主函数里的某条语句,点击“Run to Cursor Line”也至少要点击三次);若是将程序烧进硬件跑,程序短时间未见能工作(未测试时间过久些是否会工作),串口助手收不到输出的数据。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题很简单,但困扰了小白好几天,写下来希望其他初学者遇到相同问题时有所参考。
32初学之路,还请各位大神大大多多指教。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
后面的发送才正常。
初学过程,还请多赐教