下面的这几个函数和测试是我在Stm32串口编程中遇到的一些对新人极不友好 的函数,但是学会之后非常的好用!!写好一次,以后可以直接复制粘贴了!!
再说一遍,这几个函数主要是围绕串口输出的哦。
最后一部分是我自己编写的输出函数,模仿printf就直接输出了,虽然也可以函数重定向,但是重定向不如这种方式灵活。
串口初始化就直接跳过,例程网上非常丰富。
//当出现可变数量的入参时的va_list、va_start
va_list ap; 定义一个va_list变量ap
va_start(ap,v);执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即ap指向第一个可变参数在堆栈的地址。
va_arg(ap,t) , ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。
ap+= sizeof(t类型),让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型指针,这正是第一个可变参数在堆栈里的地址。然后
用取得这个地址的内容。
va_end(ap); 清空va_list ap。
//示例,这个函数就是我们的串口输出函数,把串口的输出写成模拟printf的形式
void u3_printf(char* fmt, ...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);//初始化ap,ap指向第二个开始的参数(第一个参数后)
vsprintf((char*)USART3_TX_BUF,fmt,ap);//将ap根据format格式化后填入数组
va_end(ap);//结束可变参数获取
i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
//vsprintf()函数
定义函数:int vsprintf(char * str, const char * format, va_list ap);
函数说明:vsprintf()会根据参数format 字符串来转换并格式化数据, 然后将结果复制到参数str 所指的字符串数组, 直到出现字符串结束('\0')为止. 关于参数format 字符串的格式请参考printf(). va_list 用法请参考附录C 或vprintf()范例.
//关于vsprintf()可见参数一为char* const类型 所以代表指针是常量,不可以更改指针
_Success_(return >= 0)
_Check_return_opt_ _CRT_INSECURE_DEPRECATE(vsprintf_s)
_CRT_STDIO_INLINE int __CRTDECL vsprintf(
_Pre_notnull_ _Always_(_Post_z_) char* const _Buffer,
_In_z_ _Printf_format_string_ char const* const _Format,
va_list _ArgList
)
//经过测试,只能在参数一的位置上放一个char型数组的头地址,即数组名
//附加在visualStudio2019中的测试代码,参数一不可以为char * p;或者char * const p;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
void u3_printf(char* fmt, ...);
int main() {
u3_printf("hi---%s","hhhhhha");
return 0;
}
void u3_printf(char* fmt, ...)
{
unsigned char USART3_TX_BUF[600];
unsigned int i, j;
va_list ap;
va_start(ap, fmt);//初始化ap,ap指向第二个开始的参数(第一个参数后)
vsprintf((char*)USART3_TX_BUF, fmt, ap);//将ap根据format格式化后填入数组,sizeof(USART3_TX_BUF)
va_end(ap);//结束可变参数获取
i = strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for (j = 0; j < i; j++) //循环发送数据
{
//while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); //循环发送,直到发送完毕
//USART_SendData(USART3, USART3_TX_BUF[j]);
printf("%c", USART3_TX_BUF[j]);
}
}
//sprintf():
函数功能:把格式化的数据写入某个字符串
函数原型:int sprintf( char *buffer, const char *format [, argument] … );
返回值:字符串长度(strlen)
//例子:
char* who = “I”;
char* whom = “CSDN”;
sprintf(s, “%s love %s.”, who, whom); //产生:"I love CSDN. " 这字符串写到s中
sprintf(s, “%10.3f”, 3.1415626); //产生:" 3.142"
//strstr()函数
strstr ()函数:
strstr (str1,str2)函数用于判断字符串str2是否是str1的子串。如果是,则返回str2在str1中首次出现的地址:否则,返回NULL。
/*
*我写的串口格式化输出函数 u_printf()这个更简单一些
*/
void u_printf(USART_TypeDef * USARTx, const char * format, ...){
int length;
int j;
va_list ap;
va_start(ap, format);//初始化
vsprintf((char *)USART3_TX_BUF, format, ap);
va_end(ap);//清除
length = strlen((const char *)USART3_TX_BUF);
for(j = 0; j < length; j++){
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)==RESET);//等待
USART_SendData(USARTx, USART3_TX_BUF[j]);
}
}
//或者这个,麻烦一些
void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
{
const char *s;
int d;
char buf[16];
va_list ap;
va_start(ap, Data);
while ( * Data != 0 ) // 判断是否到达字符串结束符
{
if ( * Data == 0x5c ) //'\'
{
switch ( *++Data )
{
case 'r': //回车符
USART_SendData(USARTx, 0x0d);
Data ++;
break;
case 'n': //换行符
USART_SendData(USARTx, 0x0a);
Data ++;
break;
default:
Data ++;
break;
}
}
else if ( * Data == '%')
{ //
switch ( *++Data )
{
case 's': //字符串
s = va_arg(ap, const char *);
for ( ; *s; s++)
{
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
}
Data++;
break;
case 'd':
//十进制
d = va_arg(ap, int);
itoa(d, buf, 10);
for (s = buf; *s; s++)
{
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
}
Data++;
break;
default:
Data++;
break;
}
}
else USART_SendData(USARTx, *Data++);
while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );
}
}
就这些~
好用的话点个赞吧~