加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

STM32中printf与scanf的实现问题

(2018-03-13 16:11:16)
标签:

it

分类: STM32、LPC1768及其他单片机版
#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"的问题都有待进一步探讨。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有