2017-05-29 22 views
0

私はGNSSの追跡の目的でSTM32L4 MCU(超低消費電力Cortex-M4)をベースにしたボードを用意しています。私はRTOSを使用しないので、カスタムスケジューラを使用します。コンパイラと環境はKEIL uVision 5(コンパイラ5.05と5.06、動作は変更されません)strtolとstrtodのARMCC 5の最適化

MCUはプレーンなUARTを介してGNSSモジュールと通信し、プロトコルはNMEAとATの組み合わせです。 GNSSの位置は、プレーンテキストとして与えられます。プレーンテキストは、浮動小数点/二重座標のペアに変換する必要があります。 テキストからdouble/float値を取得するには、strtod(またはstrtof)を使用します。 文字列操作は、UART RXとは異なる別のバッファで行われることに注意してください。

UART上の緯度のための典型的な文字列は、私は、次の式を使用して、度で絶対位置を取得するために42°56.45783'

を意味

4256.45783

ある

42 + 56.45783/60

最適化がない場合、コードは正常に動作し、位置は正しく変換されます。レベル1の最適化(またはそれ以上)をオンにすると、標準のCライブラリを使用すると整数部分(この例では42)を変換でき、56.45783を変換すると56までしか得られませんドット)。

標準ライブラリを削除し、ANSI Cソースライブラリからダウンロードしたカスタムstrtod関数を使用すると、単にERANGEエラーで0が得られます。

私はstrtolを使用しています。これは、L1最適化をオンにしたときに奇妙な動作をします。最初の桁が9で、変換の基数が10のとき、単に9をスキップします。

バッファに92がある場合、私はちょうど2を解析します。これを取り除くために、私は単純に記号+を数字の前に付加し、結果は常にOKです(私が知る限り)。このWAはstrtodでは動作しません。

静的、揮発性、およびスタック上の変数を使用しようとしましたが、動作は変更されません。

EDIT:

void GnssStringToLatLonDegMin(const char* str, LatLong_t* struc) 
{ 
    double dbl = 0.0; 
    dbl = strtod("56.45783",NULL); 
    if(struc != NULL) 
    { 
     struc->Axis = (float)((dbl/60.0) + 42.0); 
    } 
} 

レベル0の最適化:

559: void GnssStringToLatLonDegMin(const char* str, LatLong_t* struc) 
0x08011FEE BDF8  POP   {r3-r7,pc} 
560: { 
0x08011FF0 B570  PUSH   {r4-r6,lr} 
0x08011FF2 4605  MOV   r5,r0 
0x08011FF4 ED2D8B06 VPUSH.64  {d8-d10} 
0x08011FF8 460C  MOV   r4,r1 
561:   double dbl = 0.0; 
0x08011FFA ED9F0BF8 VLDR   d0,[pc,#0x3E0] 
0x08011FFE EEB08A40 VMOV.F32  s16,s0 
0x08012002 EEF08A60 VMOV.F32  s17,s1 
562:   dbl = strtod("56.45783",NULL); 
0x08012006 2100  MOVS   r1,#0x00 
0x08012008 A0F6  ADR   r0,{pc}+4 ; @0x080123E4 
0x0801200A F7FDFED1 BL.W   __hardfp_strtod (0x0800FDB0) 
0x0801200E EEB08A40 VMOV.F32  s16,s0 
0x08012012 EEF08A60 VMOV.F32  s17,s1 
563:   if(struc != NULL) 
564:   { 
0x08012016 B1A4  CBZ   r4,0x08012042 
565:     struc->Axis = (float)((dbl/60.0) + 42.0); 
566:   } 
0x08012018 ED9F0BF5 VLDR   d0,[pc,#0x3D4] 
0x0801201C EC510B18 VMOV   r0,r1,d8 
0x08012020 EC532B10 VMOV   r2,r3,d0 
0x08012024 F7FEF880 BL.W   __aeabi_ddiv (0x08010128) 
0x08012028 EC410B1A VMOV   d10,r0,r1 
0x0801202C ED9F0BF2 VLDR   d0,[pc,#0x3C8] 
0x08012030 EC532B10 VMOV   r2,r3,d0 
0x08012034 F7FDFFBC BL.W   __aeabi_dadd (0x0800FFB0) 
0x08012038 EC410B19 VMOV   d9,r0,r1 
0x0801203C F7FDFF86 BL.W   __aeabi_d2f (0x0800FF4C) 
0x08012040 6020  STR   r0,[r4,#0x00] 
567: } 
私はCのコードは次のようである

以下

コメントどおり、それは間違って行くところを取得するためにコードを簡素化

レベル1の最適化

557: void GnssStringToLatLonDegMin(const char* str, LatLong_t* struc) 
0x08011FEE BDF8  POP   {r3-r7,pc} 
558: { 
559:   double dbl = 0.0; 
0x08011FF0 B510  PUSH   {r4,lr} 
0x08011FF2 460C  MOV   r4,r1 
560:   dbl = strtod("56.45783",NULL); 
0x08011FF4 2100  MOVS   r1,#0x00 
0x08011FF6 A0F7  ADR   r0,{pc}+2 ; @0x080123D4 
0x08011FF8 F7FDFEDA BL.W   __hardfp_strtod (0x0800FDB0) 
561:   if(struc != NULL) 
562:   { 
0x08011FFC 2C00  CMP   r4,#0x00 
0x08011FFE D010  BEQ   0x08012022 
563:     struc->Axis = (float)((dbl/60.0) + 42.0); 
564:   } 
0x08012000 ED9F1BF7 VLDR   d1,[pc,#0x3DC] 
0x08012004 EC510B10 VMOV   r0,r1,d0 
0x08012008 EC532B11 VMOV   r2,r3,d1 
0x0801200C F7FEF88C BL.W   __aeabi_ddiv (0x08010128) 
0x08012010 ED9F1BF5 VLDR   d1,[pc,#0x3D4] 
0x08012014 EC532B11 VMOV   r2,r3,d1 
0x08012018 F7FDFFCA BL.W   __aeabi_dadd (0x0800FFB0) 
0x0801201C F7FDFF96 BL.W   __aeabi_d2f (0x0800FF4C) 
0x08012020 6020  STR   r0,[r4,#0x00] 
565: } 

これらの関数によって呼び出される__hardfp_strtodと__strtod_intの逆アセンブリを調べました。バイナリとして組み込まれているため、最適化レベルに関しては変更されません。

+0

デバッガでコードをステップ実行して、解析に何か間違いがないことを確認しましたか? –

+0

'tmpf = strtod(" 56.45783 "、NULL);'動作しますか? –

+0

@Someprogrammerdude、確かに...だからこそ私は質問を投稿しました。バッファーは正しいです(私がデバッガーから見る限り)。 – radice

答えて

0

最適化のために、strtodが機能しませんでした。 @old_timerのおかげで、自分のstrtod関数を作成しなければなりませんでした。この関数は、レベル2で設定された最適化レベルでも機能します。

double simple_strtod(const char* str) 
{ 
int8 inc; 
double result = 0.0; 
char * c_tmp; 
c_tmp = strchr(str, '.'); 
if(c_tmp != NULL) 
{ 
    c_tmp++; 
    inc = -1; 
    while(*c_tmp != 0 && inc > -9) 
    { 
     result += (*c_tmp - '0') * pow(10.0, inc); 
     c_tmp++; inc--; 
    } 
    inc = 0; 
    c_tmp = strchr(str, '.'); 
    c_tmp--; 
    do 
    { 
     result += (*c_tmp - '0') * pow(10.0,inc); 
     c_tmp--; inc++; 
    }while(c_tmp >= str); 
} 
return result; 
} 

これは、「pow」を呼び出さずにさらに巧妙なものを使用することで、さらに最適化できますが、このように完全に機能します。