×
单片机 > 单片机程序设计 > 详情

STM32中printf与scanf的实现问题

发布时间:2020-06-09 发布时间:
|

#include "sys.h"

#include "usart3.h"      

#include "stdarg.h"          

#include "stdio.h"          

#include "string.h"

#include "timer.h" 

//串口发送缓存区     

__align(8) u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];     //发送缓冲,最大USART3_MAX_SEND_LEN字节

//串口接收缓存区     

u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];                       //接收缓冲,最大USART3_MAX_RECV_LEN个字节.

//通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据.

//如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到

//任何数据,则表示此次接收完毕.

//接收到的数据状态

//[15]:0,没有接收到数据;1,接收到了一批数据.

//[14:0]:接收到的数据长度

vu16 USART3_RX_STA=0;

void USART3_IRQHandler(void)

{

    u8 res;

    if(USART3->SR&(1<<5))//接收到数据

    {

        res=USART3->DR;

        if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据

        {

            if(USART3_RX_STA

            {

                TIM7->CNT=0;                         //计数器清空

                if(USART3_RX_STA==0)                 //使能定时器7的中断

                {

                    TIM7->CR1|=1<<0;                 //使能定时器7

                }

                USART3_RX_BUF[USART3_RX_STA++]=res;  //记录接收到的值

            }

            else 

            {

                USART3_RX_STA|=1<<15;                //强制标记接收完成

            } 

        }

    }                                                       

}   

//初始化IO 串口3

//pclk1:PCLK1时钟频率(Mhz)

//bound:波特率 

void usart3_init(u32 pclk1,u32 bound)

{       

    float temp;

    u16 mantissa;

    u16 fraction;       

    temp=(float)(pclk1*1000000)/(bound*16);//得到USARTDIV,OVER8设置为0

    mantissa=temp;                         //得到整数部分

    fraction=(temp-mantissa)*16;           //得到小数部分,OVER8设置为0     

    mantissa<<=4;

    mantissa+=fraction; 

    RCC->AHB1ENR|=1<<1;               //使能PORTB口时钟  

    RCC->APB1ENR|=1<<18;              //使能串口3时钟 

   GPIO_Set(GPIOB,PIN10|PIN11,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB10,PB11,复用功能,上拉输出

     GPIO_AF_Set(GPIOB,10,7);         //PB10,AF7

    GPIO_AF_Set(GPIOB,11,7);          //PB11,AF7         

    //波特率设置

     USART3->BRR=mantissa;            // 波特率设置     

    USART3->CR1|=1<<3;                //串口发送使能  

    USART3->CR1|=1<<2;                //串口接收使能

    USART3->CR1|=1<<5;                //接收缓冲区非空中断使能    

    USART3->CR1|=1<<13;               //串口使能  

    MY_NVIC_Init(0,2,USART3_IRQn,2);  //组2,优先级0,2,最高优先级 

    TIM7_Int_Init(100-1,9000-1);      //10ms中断一次

    TIM7->CR1&=~(1<<0);               //关闭定时器7

    USART3_RX_STA=0;                  //清零 

}

//串口3,printf 函数

//确保一次发送数据不超过USART3_MAX_SEND_LEN字节

void u3_printf(char* fmt,...)  

{  

    u16 i,j;

    va_list ap;

    va_start(ap,fmt);

    vsprintf((char*)USART3_TX_BUF,fmt,ap);

    va_end(ap);

    i=strlen((const char*)USART3_TX_BUF);//此次发送数据的长度

    for(j=0;j

    {

        while((USART3->SR&0X40)==0);     //循环发送,直到发送完毕   

        USART3->DR=USART3_TX_BUF[j];  

    }

}

=================================================================

#include "sys.h"

#include "usart.h"      

//加入以下代码,支持printf函数,而不需要选择use MicroLIB      

#if 1

#pragma import(__use_no_semihosting)  

//解决HAL库使用时,某些情况可能报错的bug

int _ttywrch(int ch)    

{

    ch=ch;

    return ch;

}

//标准库需要的支持函数                 

struct __FILE 

    int handle; 

    // Whatever you require here. If the only file you are using is  

    // standard output using printf() for debugging, no file handling  

    // is required.  

}; 

// FILE is typedef’ d in stdio.h.  

FILE __stdout;       

//定义_sys_exit()以避免使用半主机模式    

void _sys_exit(int x) 

    x = x; 

//重定义fputc函数 

int fputc(int ch, FILE *f)

{      

    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   

    USART1->DR = (u8) ch;      

    return ch;

}

#endif 

//////////////////////////////////////////////////////////////////

#if EN_USART1_RX      //如果使能了接收

//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误       

u8 USART_RX_BUF[USART_REC_LEN];   //接收缓冲,最大USART_REC_LEN个字节.

//接收状态

//bit15,    接收完成标志

//bit14,    接收到0x0d

//bit13~0,    接收到的有效字节数目

u16 USART_RX_STA=0;   //接收状态标记      

  

void USART1_IRQHandler(void)

{

    u8 res;    

#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.

    OSIntEnter();    

#endif

    if(USART1->SR&(1<<5))//接收到数据

    {     

        res=USART1->DR; 

        if((USART_RX_STA&0x8000)==0)//接收未完成

        {

            if(USART_RX_STA&0x4000) //接收到了0x0d

            {

                if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

                else USART_RX_STA|=0x8000;  //接收完成了 

            }

            else //还没收到0X0D

            {    

                if(res==0x0d)USART_RX_STA|=0x4000;

                else

                {

                    USART_RX_BUF[USART_RX_STA&0X3FFF]=res;

                    USART_RX_STA++;

                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收      

                }         

            }

        }                                                    

    } 

#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.

    OSIntExit();                                               

#endif

}

#endif                                         

//初始化IO 串口1

//pclk2:PCLK2时钟频率(Mhz)

//bound:波特率 

void uart_init(u32 pclk2,u32 bound)

{       

    float temp;

    u16 mantissa;

    u16 fraction;       

    temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV@OVER8=0

    mantissa=temp;               //得到整数部分

    fraction=(temp-mantissa)*16; //得到小数部分@OVER8=0 

    mantissa<<=4;

    mantissa+=fraction; 

    RCC->AHB1ENR|=1<<0;          //使能PORTA口时钟  

    RCC->APB2ENR|=1<<4;          //使能串口1时钟 

   GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PA9,PA10,复用功能,上拉输出

     GPIO_AF_Set(GPIOA,9,7);     //PA9,AF7

    GPIO_AF_Set(GPIOA,10,7);//PA10,AF7         

    //波特率设置

     USART1->BRR=mantissa;       //波特率设置     

    USART1->CR1&=~(1<<15);       //设置OVER8=0 

    USART1->CR1|=1<<3;           //串口发送使能 

#if EN_USART1_RX                 //如果使能了接收

    //使能接收中断 

    USART1->CR1|=1<<2;           //串口接收使能

    USART1->CR1|=1<<5;           //接收缓冲区非空中断使能            

    MY_NVIC_Init(3,3,USART1_IRQn,2);//组2,最低优先级 

#endif

    USART1->CR1|=1<<13;          //串口使能

}

=================================================================

STM32中printf与scanf的重定向问题:http://blog.csdn.net/l_yankui/article/details/53452738

关于STM32中 printf 与 scanf 的重定向问题在此我仅对不使用 "USE MircoLIB" 的情况做整理(针对Keil RVMDK开发环境)。

① :首先需要在 usart.h 中包含 “stdio.h” 头文件

② :在 usart.c 中,加入如下代码块,以此避免使用半主机模式,并重定向 printf 和scanf 函数;

#if 1  

#pragma import (__use_no_semihosting_swi)  

//标准库需要的支持函数,use_no_semihosting_swi以避免使用半主机模式  

struct __FILE  

{  

    int handle;  

};  

  

FILE __stdout;  

FILE __stdin;  

//重定向Printf函数  

int fputc(int ch,FILE *f)  

{  

    return (SendChar(ch));  

}  

//重定向Scanf函数  

int fgetc(FILE *f)  

{  

    return (SendChar(GetKey()));  

    //调用scanf()在串口中输入数据时,必须以空格结束,否则无法完成发送  

}  

void _ttywrch(int ch)  

{  

    SendChar(ch);  

}  

int _ferror(FILE *f) {  

  // Your implementation of ferror   

  return EOF;  

}  

//定义_sys_exit()以避免使用半主机模式  

void _sys_exit(int return_code){  

    //x = x;  

label:goto label;  

}  

#endif  

③ :在 usart.c 中添加SendChar()与GetKey()函数

[plain] view plain copy

int SendChar(int ch)  

{  

    while(!(USART1->SR & USART_FLAG_TXE));  

    USART1->DR = (ch & 0x1FF);  

  

    return ch;  

  

int GetKey(void)  

{  

    while(!(USART1->SR & USART_FLAG_RXNE));  

    return ((int)(USART1->DR & 0X1FF));  

}  

完成以上三步,即可实现printf()函数与scanf()的串口重定向,将标准输入输出流的来源或去向改为串口。

关于第二步所使用的避免使用半主机模式的代码,其实Kei已经为我们写好了一个Retarget.c文件,在Keil/ARM/Startup目录下.

另外本文针对的开发环境为Keil RVMDK,本人在Emblocks开源开发工具中实验时,本方法是无法实现printf和Scanf的重定向的,以及在开源工具下如何"Use microLIB"的问题都有待进一步探讨。


关键字:STM32  printf  scanf

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析