私は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の逆アセンブリを調べました。バイナリとして組み込まれているため、最適化レベルに関しては変更されません。
デバッガでコードをステップ実行して、解析に何か間違いがないことを確認しましたか? –
'tmpf = strtod(" 56.45783 "、NULL);'動作しますか? –
@Someprogrammerdude、確かに...だからこそ私は質問を投稿しました。バッファーは正しいです(私がデバッガーから見る限り)。 – radice