/FollowMe/FollowMe.pnproj |
---|
0,0 → 1,0 |
<Project name="FollowMe"><Folder name="Sources"><File path="ubx.c"></File><File path="analog.c"></File><File path="button.c"></File><File path="crc16.c"></File><File path="fat16.c"></File><File path="led.c"></File><File path="main.c"></File><File path="menu.c"></File><File path="printf_P.c"></File><File path="sdc.c"></File><File path="ssc.c"></File><File path="timer0.c"></File><File path="uart0.c"></File><File path="uart1.c"></File><File path="gps.c"></File></Folder><Folder name="Header"><File path="ubx.h"></File><File path="analog.h"></File><File path="button.h"></File><File path="crc16.h"></File><File path="fat16.h"></File><File path="led.h"></File><File path="main.h"></File><File path="menu.h"></File><File path="printf_P.h"></File><File path="sdc.h"></File><File path="ssc.h"></File><File path="timer0.h"></File><File path="uart0.h"></File><File path="uart1.h"></File><File path="gps.h"></File></Folder></Project> |
/FollowMe/FollowMe.pnps |
---|
0,0 → 1,0 |
<pd><ViewState><e p="FollowMe" x="true"></e><e p="FollowMe\Header" x="true"></e><e p="FollowMe\Sources" x="true"></e></ViewState></pd> |
/FollowMe/FollowMe_MEGA644p_FOLLOWME_V0_1c_SVN.hex |
---|
0,0 → 1,1558 |
:100000000C9451040C946C040C946C040C946C04CB |
:100010000C946C040C946C040C946C040C946C04A0 |
:100020000C946C040C946C040C946C040C946C0490 |
:100030000C946C040C946C040C946C040C946C0480 |
:100040000C946C040C946C040C94F80E0C946C04DA |
:100050000C942C070C946C040C94F3060C946C0414 |
:100060000C9407170C946C040C946C040C946C04A2 |
:100070000C94A10B0C946C040C946C040D0A0D0AE6 |
:1000800048573A20466F6C6C6F772D4D65000D0A0E |
:10009000466F6C6C6F77204D650A0D536F667477F1 |
:1000A0006172653A5625642E2564256320000D0A89 |
:1000B0002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D70 |
:1000C0002D2D2D2D2D2D2D2D2D2D2D2D2D2D000DAD |
:1000D0000A005B25695D005B25695D002B20466F8A |
:1000E0006C6C6F77204D65202B0048573A20466F87 |
:1000F0006C6C6F772D4D650053573A2025642E2583 |
:100100006425630020202020202020202020004E75 |
:100110006F204750532064617461004C6F6E3A2029 |
:1001200020202020202020202020202020202000EF |
:100130004C61743A202020202020202020202020E4 |
:100140002020202000416C743A20202020202020F4 |
:1001500020202020202020202000536174733A2585 |
:10016000303264204669783A4E6F6E650053617490 |
:10017000733A25303264204669783A3244202000B0 |
:10018000536174733A25303264204669783A3344B7 |
:10019000202000536174733A253032642046697818 |
:1001A0003A3F3F2020004C6F6E3A20256325642E95 |
:1001B000252E3364252E336420646567004C6174FA |
:1001C0003A20256325642E252E3364252E336420A2 |
:1001D00064656700416C743A202563253034642ED1 |
:1001E000252E303364206D004E6F2047505320641D |
:1001F000617461005370656564204E3A20202020B0 |
:100200002020202020202020005370656564204598 |
:100210003A20202020202020202020202000537061 |
:1002200065656420543A20202020202020202020B2 |
:10023000202000536174733A253032642046697877 |
:100240003A4E6F6E6500536174733A253032642004 |
:100250004669783A3244202000536174733A25305D |
:100260003264204669783A33442020005361747325 |
:100270003A25303264204669783A3F3F20200053C7 |
:1002800070656564204E3A20252B346420636D2F01 |
:100290007300537065656420453A20252B34642033 |
:1002A000636D2F7300537065656420543A20252BCD |
:1002B000346420636D2F73004750532055544320FE |
:1002C00054696D650020202020202020202020203F |
:1002D0002020202020202020200020204E6F20746D |
:1002E000696D652064617461212020202020002038 |
:1002F00020202020202020202020202020202020FE |
:10030000202020002020202020202020202020200D |
:10031000202020202020202000446174653A2025E0 |
:100320003032692F253032692F2530346900546905 |
:100330006D653A20253032693A253032693A2530E8 |
:1003400032692E25303369000000211042206330CD |
:100350008440A550C660E770088129914AA16BB11D |
:100360008CC1ADD1CEE1EFF13112100273325222C5 |
:10037000B5529442F772D662399318837BB35AA36D |
:10038000BDD39CC3FFF3DEE3622443342004011495 |
:10039000E664C774A44485546AA54BB528850995BD |
:1003A000EEE5CFF5ACC58DD5533672261116300665 |
:1003B000D776F6669556B4465BB77AA7199738870D |
:1003C000DFF7FEE79DD7BCC7C448E5588668A77825 |
:1003D0004008611802282338CCC9EDD98EE9AFF95D |
:1003E000488969990AA92BB9F55AD44AB77A966A05 |
:1003F000711A500A333A122AFDDBDCCBBFFB9EEBAD |
:10040000799B588B3BBB1AABA66C877CE44CC55CD4 |
:10041000222C033C600C411CAEED8FFDECCDCDDDFC |
:100420002AAD0BBD688D499D977EB66ED55EF44EA4 |
:10043000133E322E511E700E9FFFBEEFDDDFFCCF4C |
:100440001BBF3AAF599F788F8891A981CAB1EBA1A0 |
:100450000CD12DC14EF16FE18010A100C230E3201C |
:100460000450254046706760B9839893FBA3DAB3C4 |
:100470003DC31CD37FE35EF3B1029012F322D2326C |
:100480003542145277625672EAB5CBA5A895898594 |
:100490006EF54FE52CD50DC5E234C324A0148104BC |
:1004A0006674476424540544DBA7FAB79987B89764 |
:1004B0005FE77EF71DC73CD7D326F2369106B0160C |
:1004C00057667676154634564CD96DC90EF92FE924 |
:1004D000C899E9898AB9ABA944586548067827685C |
:1004E000C018E1088238A3287DCB5CDB3FEB1EFB04 |
:1004F000F98BD89BBBAB9ABB754A545A376A167AAC |
:10050000F10AD01AB32A923A2EFD0FED6CDD4DCDD3 |
:10051000AABD8BADE89DC98D267C076C645C454CFB |
:10052000A23C832CE01CC10C1FEF3EFF5DCF7CDFA3 |
:100530009BAFBABFD98FF89F176E367E554E745E4B |
:10054000932EB23ED10EF01E4572726F722025308E |
:1005500032582072656164696E672064617461203D |
:1005600066726F6D2073642063617264202852315B |
:100570003D25303258292E0D0A000D0A20204D61EC |
:100580006E7566616374757265722049443A202500 |
:10059000690D0A0020204170706C69636174696F95 |
:1005A0006E2049443A2025730D0A00202050726FB6 |
:1005B00064756374204E616D653A2025730D0A00E1 |
:1005C000202050726F64756374205265762E3A2035 |
:1005D00025692E25690D0A00202053657269616C1A |
:1005E000204E6F2E3A200025303258000D0A002090 |
:1005F000204D616E756661632E20446174653A20FA |
:1006000025692F25690D0A0D0A000D0A2053534351 |
:1006100020696E69742E2E2E006F6B000D0A205318 |
:10062000444320696E69742E2E2E007265736574C2 |
:100630002074696D656F75740042616420636D6438 |
:10064000382052313D253032582E004261642063FB |
:100650006D643820523720636865636B2070617465 |
:100660007465726E2E0D0A004361726420697320F6 |
:10067000696E636F6D70617469626C6520746F2060 |
:10068000332E33562E0D0A0042616420636D6435AB |
:100690003820523120253032782E004E6F74206180 |
:1006A0006E2053442D434152442E00436172642016 |
:1006B000697320696E636F6D70617469626C652027 |
:1006C000746F20332E33562E004261642041636DD7 |
:1006D0006434312052313D253032582E00496E6944 |
:1006E000742074696D656F75742E00496E6974208D |
:1006F0006572726F722E004572726F72207365742C |
:1007000074696E6720626C6F636B206C656E6774D2 |
:100710006820746F203531322E004572726F72205E |
:1007200072656164696E67204349442E0D0A004575 |
:1007300072726F722072656164696E672043534400 |
:100740002E006F6B0D0A000D0A202053442D4341EB |
:1007500052442056312E78000D0A202053442D4358 |
:100760004152442056322E30206F72206C617465E5 |
:1007700072000D0A20204361706163697479203D25 |
:10078000202569204D42004E6F204361726420692C |
:100790006E20536C6F742E000D0A20534443206466 |
:1007A00065696E69742E2E2E006F6B004572726F34 |
:1007B0007220253032582077726974696E67206420 |
:1007C00061746120746F20736420636172642028F7 |
:1007D000523D25303258292E0D0A000D0A2046417F |
:1007E00054313620696E69742E2E2E0053442D43E9 |
:1007F00061726420636F756C64206E6F7420626533 |
:1008000020696E697469616C697A65642E0045724D |
:10081000726F722072656164696E6720746865200A |
:100820004D42522E004572726F722072656164698A |
:100830006E6720746865205642522E005642523A26 |
:1008400020536563746F722073697A65206E6F74CC |
:1008500020737570706F727465642E005642523A40 |
:1008600020426164206E756D626572206F66207330 |
:100870006563746F72732E005642523A2050617253 |
:10088000746974696F6E20697374206E6F7420468A |
:100890004154313620747970652E00202E2E2E6F33 |
:1008A0006B0011241FBECFEFD0E1DEBFCDBF13E040 |
:1008B000A0E0B1E0EAE7FEE502C005900D92A43C9D |
:1008C000B107D9F71DE0A4ECB3E001C01D92A4303C |
:1008D000B107E1F70C946E040C940000CFEFD0E167 |
:1008E000DEBFCDBFF89484B7877F84BF809160005E |
:1008F000886180936000109260000E941D13289808 |
:100900000E94C50E0E946C060E94AD140E94380B16 |
:100910000E94E7160E94B61778940E94012A289A2E |
:1009200029980E94012A8CE790E09F938F931F9251 |
:100930000E94300C0F900F900F9083E690E09F93F1 |
:100940008F9381E090E09F938F931F921F928EE888 |
:1009500090E09F938F931F920E94300C8DB79EB7AB |
:1009600009960FB6F8949EBF0FBE8DBF8EEA90E039 |
:100970009F938F931F920E94300C0F900F900F90B7 |
:100980008FEC90E09F938F931F920E94300C0E94F7 |
:10099000650F88EE93E00E94440F9093D1038093FB |
:1009A000D0030F900F900F900E94382E0E94C11715 |
:1009B000882309F425C188EC90E09093E5038093A7 |
:1009C000E4032091D203822F9927019709F43BC1B8 |
:1009D00021E02093D203822F99278130910509F4D9 |
:1009E00017C1029709F438C181E08093D203809146 |
:1009F000C203882309F4F2C08091650690916606CF |
:100A000090937505809374058091630690916406B8 |
:100A100090937705809376058091590690915A06B8 |
:100A2000909379058093780580915B0690915C06A0 |
:100A300090937B0580937A05809161069091620680 |
:100A400090937D0580937C05809157069091580680 |
:100A500090937F0580937E0580915D0690915E0660 |
:100A6000909381058093800580915F069091600648 |
:100A700090938305809382058091000190910101FC |
:100A80009C01220F331F280F391F8091610690911E |
:100A90006206002496958795079496958795079406 |
:100AA000982F802D60E771E00E94EC2E260F371FF3 |
:100AB00036952795369527953093010120930001AF |
:100AC00030938505209384054091CC035091CD034C |
:100AD000443651050CF0AFC0253531050CF0B0C0DF |
:100AE000E2E0F0E0F093CF03E093CE034F5F5F4F7F |
:100AF0005093CD034093CC03F0939505E093940578 |
:100B000050939705409396056091CA037091CB036B |
:100B10006E177F070CF084C0443651050CF480C07A |
:100B2000A091E403B091E503109709F079C080919A |
:100B3000C8039091C903C3E0893E9C078CF084E60A |
:100B400090E09093E5038093E4036F5F7F4F709391 |
:100B5000CB036093CA03B093C903A093C803CD012C |
:100B600065E08C3D96072CF401969093C903809321 |
:100B7000C803EE0FFF1FE295F295F07FFE27E07F9E |
:100B8000FE272E173F0724F02A3431050CF04DC004 |
:100B9000443651050CF449C080E093E0909308037B |
:100BA000809307038091E4039091E503892B31F44E |
:100BB00080E797E19093E5038093E4038091C40379 |
:100BC0009091C50382609093C5038093C4031092F3 |
:100BD000C20380917A00886C80937A000E94420957 |
:100BE0000E94D9098091C4039091C503892BB1F06B |
:100BF00028980E94382E0E94C117882309F0DBCE66 |
:100C00002091D203822F99278130910509F0E9CEF6 |
:100C100029981092C7031092C603E9CE289AC4CE31 |
:100C20008091C8039091C9039BCF8091C403909198 |
:100C3000C5038D7FC8CFE091CE03F091CF035CCF89 |
:100C4000E3E0F0E04FCF8091C4039091C503892B7E |
:100C500009F0C1CE22E0BDCE8091D0039091D103A6 |
:100C60000E944D0F882309F4C2CEC0914B05C130BC |
:100C700099F08AEF90E00E94440F9093D103809303 |
:100C8000D00385B192E0892785B981E090E0909307 |
:100C9000C7038093C603ABCE88EE93E00E94440F57 |
:100CA0009093D1038093D0038FEF9FEF90934D05E6 |
:100CB00080934C05C0934E058CE380934F051092B2 |
:100CC0005005109251051092520510925305109242 |
:100CD0005405C093D903D5CFCF93CFB72AE230E0E4 |
:100CE000F8948091C1008F778093C1008091C100FA |
:100CF0008F7B8093C100589A5098599A519A832FAC |
:100D000099278093C5002093C4008091C000826021 |
:100D10008093C00088E18093C1008091C2008F77EA |
:100D20008093C2008091C2008F7B8093C20080912B |
:100D3000C2008F7D8093C2008091C2008F7E80931D |
:100D4000C2008091C200877F8093C2008091C10061 |
:100D50008B7F8093C1008091C20084608093C20029 |
:100D60008091C20082608093C2008091C00087FFA2 |
:100D70000CC08091C6008091C000882334F480911B |
:100D8000C6008091C0008823A4F38091C1008068D0 |
:100D90008093C1008091C10080648093C1008091E4 |
:100DA0000203909103030E94440F90934B0680939B |
:100DB0004A061092D7031092D5031092D4031092D2 |
:100DC000D30381E08093050310925D0580935E0557 |
:100DD00082E0809361058AE080935F051092600550 |
:100DE000CFBFCF9108951F920F920FB60F9211248B |
:100DF0008F939F93EF93FF93809105038823A9F42A |
:100E00008091DF039091E0030196FC01EC54FA4FCE |
:100E1000E081ED3079F08639910561F09093E0033F |
:100E20008093DF03E093C6000EC01092E00310929F |
:100E3000DF0309C01092E0031092DF0381E080938A |
:100E40000503E093C600FF91EF919F918F910F9062 |
:100E50000FBE0F901F9018951F920F920FB60F9212 |
:100E600011242F933F934F935F938F939F93AF934F |
:100E7000BF93CF93DF93EF93FF935091C600809180 |
:100E8000D703882309F073C04091E3034423B9F4E6 |
:100E90005332E1F05D3011F1E42FFF27E755FB4FAE |
:100EA00050834F5F4093E3038091E1039091E2030D |
:100EB000850F911D9093E2038093E10358C046395A |
:100EC00048F38093E3038093D70351C05093A90460 |
:100ED00081E08093E30383E290E0ECCFA42FBB2773 |
:100EE000A755BB4FFD01329780812091E1033091DE |
:100EF000E203281B3109ED0121978881281B310964 |
:100F0000C9019F709093E2038093E1030024880F4E |
:100F1000991F001C880F991F001C892F902D982F56 |
:100F2000935C2F733070235C8081891729F01092B5 |
:100F3000D7031092E3031BC088818217C1F75C932B |
:100F40004F5F4093D60381E08093D7038091AB0439 |
:100F5000823579F788E190E02CE00FB6F894A895F7 |
:100F6000809360000FBE209360001092E303FF9116 |
:100F7000EF91DF91CF91BF91AF919F918F915F9151 |
:100F80004F913F912F910F900FBE0F901F9018958A |
:100F9000AC01A0E0B0E09D01A817B90748F4E4EB6C |
:100FA000F5E08191280F311D1196A417B507C8F3FC |
:100FB0003F70FD01EC54FA4FC9010024880F991FBE |
:100FC000001C880F991F001C892F902D835C808343 |
:100FD0001196FD01EC54FA4F2F733070822F835C11 |
:100FE0008083AB54BA4F8DE08C93109205038091AF |
:100FF000B4058093C6000895BF92CF92DF92EF921E |
:10100000FF920F931F93CF93DF93CDB7DEB72C855D |
:101010009D857E85BB2483E28093B4059F59909380 |
:10102000B5052093B60503E010E0772309F4ADC0C1 |
:10103000CF84D88843E1E42EF12CEC0EFD1EA98963 |
:10104000BA897150109709F4A0C0109709F43CC0F8 |
:10105000F601EB0DF11DB3949081119789F477237C |
:10106000A1F1F70122E030E0E20EF31ED701C080CB |
:10107000D180E20EF31E0D90BC91A02DBB247150C7 |
:10108000109719F1F601EB0DF11DB3944081119702 |
:1010900009F466C01097D1F0F601EB0DF11DB39481 |
:1010A00060811197A1F4772391F0F7013296D7016F |
:1010B000CD90DC9032E0E32EF12CEE0EFF1EA081ED |
:1010C000B181BB24715003C090E040E060E0F801C2 |
:1010D000EC54FA4F892F86958695835C80830F5F49 |
:1010E0001F4FF801EC54FA4F892F992783709070A5 |
:1010F00082959295907F9827807F982755279A010F |
:10110000329522952F7023273F702327822B835CF3 |
:1011100080830F5F1F4FF801EC54FA4F4F705070EF |
:10112000440F551F440F551F862F99270024880F01 |
:10113000991F001C880F991F001C892F902D842B4C |
:10114000835C80830F5F1F4FF801EC54FA4F6F737D |
:10115000635C60830F5F1F4F109709F076CF15C057 |
:10116000772309F4B3CFF701A2E0B0E0EA0EFB1E4B |
:10117000D701C080D18022E030E0E20EF31E0D9056 |
:10118000BC91A02DBB24715085CFC8010E94C80717 |
:10119000DF91CF911F910F91FF90EF90DF90CF9053 |
:1011A000BF900895A3E07A2F4091D603465009F4EA |
:1011B0005FC0E72FFF27E755FB4F80818D537F5F8F |
:1011C000E72FFF27E755FB4F20812D537F5FE72F48 |
:1011D000FF27E755FB4F30813D537F5FE72FFF2708 |
:1011E000E755FB4F60816D537F5F9927880F991FEB |
:1011F000880F991F522F52955F70582B822F992775 |
:101200008F70907082959295907F9827807F982715 |
:10121000232F26952695282B832F9927837090704E |
:101220000024969587950794969587950794982F0F |
:10123000802D682B41504F3FD9F0EA2FFF27E7550B |
:10124000FB4F5083AF5F41504F3F91F0EA2FFF2794 |
:10125000E755FB4F2083AF5F41504F3F49F0EA2FE6 |
:10126000FF27E755FB4F6083AF5F442309F0A1CF11 |
:101270008CEA94E09093D5038093D403A350A09379 |
:10128000D30308958091D703882309F408950E9419 |
:10129000D2088091AA048B36E1F18091AB049927A2 |
:1012A0008736910591F1883691058CF48136910548 |
:1012B00009F447C08436910509F451C01092D50352 |
:1012C0001092D4031092D3031092D70308958C3652 |
:1012D000910549F18D369105FCF48836910571F739 |
:1012E0008FEF80930403E091D403F091D5038081C4 |
:1012F0009091EA03892B8093EA03882311F01092DE |
:10130000D80381E08093DD03D9CF81E08093DB03B4 |
:10131000D5CF8091AB04C1CF8637910579F681E0B6 |
:101320008093DE03CBCF8FEF80930403E091D4034F |
:10133000F091D50380818093EB0381E08093DC03FF |
:10134000BDCFE091D403F091D503E081E032D8F035 |
:101350008FE1809306038FEF80930403AFCFE0917A |
:10136000D403F091D50390818AE0989FC0011124A5 |
:101370009093030380930203892B09F49FCF81E0AC |
:101380008093DA039BCFE0930603E5CFCF93C82F7A |
:101390008A3029F08091C00085FFFCCF04C08DE029 |
:1013A0000E94C609F7CFC093C60080E090E0CF91BD |
:1013B00008951F9380910503882309F457C18091F4 |
:1013C000DE03882309F066C08091DD03882329F0BD |
:1013D00080910503882309F0E1C08091DC03882314 |
:1013E00029F080910503882309F0ADC02091060300 |
:1013F0002F3F61F180E190E09F938F93822F992797 |
:1014000082959295907F9827807F98278E5F9E4F38 |
:101410009F938F9381E090E09F938F9386E093E07A |
:101420009F938F9382E08F938AE08F9381E48F93D1 |
:101430000E94FC078FEF809306032DB73EB7255F10 |
:101440003F4F0FB6F8943EBF0FBE2DBF8091DB0318 |
:10145000882329F080910503882309F059C08091E1 |
:10146000020390910303892B09F0CBC08091DA032A |
:10147000882329F080910503882309F0CCC080914E |
:10148000D903882309F4F2C080910503882309F465 |
:10149000EDC021C080910503882309F495CF8AE02F |
:1014A00090E09F938F938DE595E09F938F9381E0DC |
:1014B0008F938AE08F9386E58F930E94FC071092AA |
:1014C000DE032DB73EB7295F3F4F0FB6F8943EBFFE |
:1014D0000FBE2DBF79CF8EE190E09F938F938FE366 |
:1014E00095E09F938F9381E08F9312E01F9383E7A2 |
:1014F0008F930E94FC0710934B051092D9038DB770 |
:101500009EB707960FB6F8949EBF0FBE8DBFAEC0B4 |
:101510008BE090E09F938F9387E695E09F938F9366 |
:1015200081E08F938AE08F9387E48F930E94FC077A |
:101530001092DB038DB79EB707960FB6F8949EBF47 |
:101540000FBE8DBF8CCF0E946E0F80E590E09F9301 |
:101550008F9389E093E09F938F9321E030E03F9356 |
:101560002F9389E593E09F938F933F932F938BEE77 |
:1015700093E09F938F9383E08F938AE08F938CE423 |
:101580008F930E94FC071092DC038DB79EB70F96D5 |
:101590000FB6F8949EBF0FBE8DBF28CF0E946E0F6E |
:1015A00084E190E09F938F939091D80384E1989F7A |
:1015B000C0011124875F9C4F9F938F9381E090E03F |
:1015C0009F938F9388ED93E09F938F9382E08F9307 |
:1015D0008AE08F9388E48F930E94FC078091D80360 |
:1015E0008F5F2DB73EB7255F3F4F0FB6F8943EBFD4 |
:1015F0000FBE2DBF8430A8F11092D8031092DD03E6 |
:10160000ECCE80914A0690914B060E944D0F8823A4 |
:1016100009F030CF2BCF82E490E09F938F9382E745 |
:1016200095E09F938F9381E08F938AE08F9384E47A |
:101630008F930E94FC0780910203909103030E9404 |
:10164000440F90934B0680934A061092DA032DB70D |
:101650003EB7295F3F4F0FB6F8943EBF0FBE2DBF78 |
:101660000ECF8093D8031092DD03B7CE1F9108955B |
:101670004FB72AE230E0F8948091C9008F778093C9 |
:10168000C9008091C9008F7B8093C9008091C900F7 |
:101690008F7D8093C9005A9A52985B9A539A832FF0 |
:1016A00099278093CD002093CC008091C800826060 |
:1016B0008093C80088E18093C9008091CA008F7729 |
:1016C0008093CA008091CA008F7B8093CA0080916A |
:1016D000CA008F7D8093CA008091CA008F7E80935C |
:1016E000CA008091CA00877F8093CA008091C90098 |
:1016F0008B7F8093C9008091CA0084608093CA0068 |
:101700008091CA0082608093CA008091C80087FFE0 |
:101710000CC08091CE008091C800882334F4809161 |
:10172000CE008091C8008823A4F38091C90080680E |
:101730008093C9008091C90080648093C9004FBF25 |
:1017400008951F920F920FB60F9211242F933F937B |
:101750004F935F936F937F938F939F93AF93BF93B9 |
:10176000EF93FF938091CE000E94B515FF91EF910A |
:10177000BF91AF919F918F917F916F915F914F91A9 |
:101780003F912F910F900FBE0F901F901895982F9B |
:1017900080914C06813031F0892F0E94C609282F94 |
:1017A00033270CC08091E903E82FFF27E75FFC4F48 |
:1017B00090838F5F8093E90321E030E0C9010895B1 |
:1017C0000F931F93CF93DF938C01EB01672B71F085 |
:1017D000F80181918F010E94C70B219739F0F80120 |
:1017E00081918F010E94C70B219791F7DF91CF91D3 |
:1017F0001F910F9108950F931F93CF93DF938C0147 |
:10180000EB01672B81F0F8010F5F1F4F84910E945D |
:10181000C70B219741F0F8010F5F1F4F84910E9481 |
:10182000C70B219781F7DF91CF911F910F910895F9 |
:10183000CF93C82F181634F480E20E94C70BC15012 |
:101840001C16D4F3CF910895CF93C82F181634F4F3 |
:1018500080E30E94C70BC1501C16D4F3CF910895AA |
:101860002F923F924F925F926F927F928F929F92B0 |
:10187000AF92BF92CF92DF92EF92FF920F931F939E |
:10188000CF93DF93CDB7DEB7E0970FB6F894DEBF06 |
:101890000FBECDBF26968FAD26972896EEADFFAD35 |
:1018A000289788249924540180934C0648E4C42E38 |
:1018B000D12CCC0EDD1E7F01C701F701149111233D |
:1018C00031F0153221F00894E11CF11CF6CFB7017C |
:1018D000681B790B09F095C0112309F43DC20894E7 |
:1018E000E11CF11C1FA63FA4232C0FEF39A6F70122 |
:1018F0000894E11CF11C14911537C9F1812F806205 |
:101900008837A9F1103209F440C0133209F48DC0B0 |
:101910001A3209F479C01D3209F476C01B32C9F1BC |
:101920001E32C9F1103309F48AC0812F81538930E6 |
:1019300008F07EC060E070E0CB01880F991F880F2F |
:10194000991F880F991F860F971F680F791F610FC6 |
:10195000711D60537040F7010894E11CF11C149153 |
:10196000812F80538A3040F3262E153739F630FE0A |
:101970003DC0F60184E090E0C80ED91E80809180C1 |
:10198000A280B380103209F0C0CF89A5882309F066 |
:10199000AECF19A7ACCFF7010894E11CF11C14914C |
:1019A0001A3209F451C060E070E014C0CB01880F16 |
:1019B000991F880F991F880F991F860F971F680F0F |
:1019C000791F610F711D60537040F7010894E11C8D |
:1019D000F11C1491812F80538A3040F3EFEF6F3F59 |
:1019E0007E0714F46FEF7FEF062F86CFF60182E0BB |
:1019F00090E0C80ED91E808191814C01AA24BB249D |
:101A000081CF0E94FB0B68CF1A3251F4F60182E0BD |
:101A100090E0C80ED91E208022200CF068CF2194BF |
:101A200090E1392AEFED3E2262CF98E0392A5FCF6C |
:101A30001836C9F01C36D1F481E0382A58CF34FC6E |
:101A400056CFF0E23F2A53CFF60182E090E0C80E75 |
:101A5000D91E608171819FEF6F3F790714F46FEF9A |
:101A60007FEF062F44CF24E0322A41CF133609F40A |
:101A70004DC1143409F41FC1143609F41CC11936C0 |
:101A800009F419C11F3409F40FC11F3609F40CC140 |
:101A9000103709F4F9C0133709F4B2C0153509F449 |
:101AA000ECC0153709F4E9C0183509F444C01837FB |
:101AB00009F441C0112309F44FC1CE0101969DA73D |
:101AC0008CA7198381E0482E19A6632C7724042D56 |
:101AD0005FA4541857FC2DC029A5222331F10F5FB4 |
:101AE000050DC30180739070892B09F42FC1222347 |
:101AF00009F027C166FC1BC1C301807390708097F9 |
:101B000009F410C1852D0E94240C842D992787FD8E |
:101B10009095BC018CA59DA50E94E00B64FECCCEE7 |
:101B2000822D801B0E94180CC7CE66FED9CF0E5F97 |
:101B3000D7CF5524D1CFE0E1EEA733FE07C0811403 |
:101B40009104A104B10411F0F0E43F2A19A60FA7F3 |
:101B500007FD02C02FED3222CE0189969DA78CA7EA |
:101B600081149104A104B10419F49FA5992361F192 |
:101B7000EEA54E2E55246624772418AA84149504C5 |
:101B8000A604B70410F0F1E0F8ABC501B401A3015D |
:101B900092010E94002FDC01CB01082F8A30F0F463 |
:101BA000005DECA5FDA50293FDA7ECA7C501B4015E |
:101BB000A30192010E94002F49015A01F8A9FF23B5 |
:101BC000E1F62EA5283079F0632C7724CE0101961A |
:101BD000482EFCA54F1A28E2420E79CF095A183533 |
:101BE00001F70F7DDECF632C772463FEEFCF003348 |
:101BF00069F380E3ECA5FDA58293FDA7ECA7E6CFF2 |
:101C0000F60182E090E0C80ED91E0190F081E02D2F |
:101C1000FDA7ECA7EF2B81F4FE013196FDA7ECA701 |
:101C200088E289838EE6818385E78B838CE68C83CB |
:101C30008D8389E28E831F8207FD15C0802F99272F |
:101C400087FD9095AC0160E070E08CA59DA50E9499 |
:101C5000C02E009731F0482EFCA54F1A04150CF049 |
:101C600033CF402E31CFECA5FDA501900020E9F740 |
:101C700031974E2E2CA5421A27CF153511F481E04D |
:101C8000382A9AE09EA762CFF60182E090E0C80E63 |
:101C9000D91E808191814C01AA24BB2490E19EA78A |
:101CA000E0E43E2A18E752CF1F3411F4E1E03E2A67 |
:101CB000F8E0FEA74BCF143411F491E0392A30FE3E |
:101CC00018C0F60184E090E0C80ED91E8080918093 |
:101CD000A280B380B7FE0AC0B094A0949094809480 |
:101CE000811C911CA11CB11C8DE289A79AE09EA7C2 |
:101CF0002ECFF60182E090E0C80ED91E808191813E |
:101D00004C01AA2497FCA094BA2CE4CFFE01319692 |
:101D1000FDA7ECA7F60182E090E0C80ED91E8081F5 |
:101D20008983D0CE822D801B0E94240CEBCE80E3D1 |
:101D30008AA71BA762E070E0CE018A960E94E00BA2 |
:101D4000DBCE61E070E0CE018996F8CF822D801B5A |
:101D50000E94180C29A5CBCEE0960FB6F894DEBFF2 |
:101D60000FBECDBFDF91CF911F910F91FF90EF90EC |
:101D7000DF90CF90BF90AF909F908F907F906F90AB |
:101D80005F904F903F902F9008959FB7F8943F9A9F |
:101D9000479884B58C7084BD85B5877385BD85B53E |
:101DA000887F826085BD16BC80916E00897F80939C |
:101DB0006E0080916E00816080936E0010924E06DE |
:101DC00010924D0610924F06109250061092510636 |
:101DD0001092520610925306109255061092540615 |
:101DE000109256061092E7031092E6039FBF0895E3 |
:101DF0001F920F920FB60F9211242F933F938F9340 |
:101E00009F938091E80381508F3F41F18093E803D5 |
:101E10008091E4039091E503892BB9F08091E4036C |
:101E20009091E50301979093E5038093E4038091FB |
:101E3000E4039091E5032091070330910803822386 |
:101E40009323892B49F4479816C08FEF9FEF909307 |
:101E5000080380930703F7CF479A0DC089E080936A |
:101E6000E8038091E6039091E70301969093E703DE |
:101E70008093E603CDCF9F918F913F912F910F904B |
:101E80000FBE0F901F9018952091E6033091E70345 |
:101E9000280F391FC901019708952091E603309159 |
:101EA000E703821B930B892F990F990B8078089574 |
:101EB000CF93DF930E94440FEC01CE010E944D0F9F |
:101EC0008823D9F3DF91CF91089590E2E9E0F3E020 |
:101ED0008FE49193815087FFFCCF08959F92AF923A |
:101EE000BF92CF92DF92EF92FF920F931F93CF9307 |
:101EF000DF932091EA0320FF08C08091EB03882341 |
:101F000009F460C081508093EB0321FF0AC09091D7 |
:101F1000EB0380915903981709F4F7C19F5F9093E1 |
:101F2000EB03822F992780FF02C081FD48C00E94E9 |
:101F3000650F9091EB0380915903891718F48093F2 |
:101F4000EB03982F9A3088F581E18093E903892F7C |
:101F500099279F938F9382ED90E09F938F9381E0D9 |
:101F60008F930E94300C0F900F900F900F900F9056 |
:101F70009091EB03E92EFF2481E0E816F10439F19A |
:101F800082E0E816F1040CF4C3C182E0E816F10423 |
:101F900009F453C183E0E816F10409F43BC29150FF |
:101FA000909359031092EB033AC380E18093E903C5 |
:101FB000892F99279F938F9387ED90E0CECF109232 |
:101FC000EB03B5CF809159038093EB039ECF8091B3 |
:101FD0001804882309F4DCC18091FB03992782301F |
:101FE000910509F458C2833091050CF006C2892B83 |
:101FF00009F4F7C21092E9038091FA0399279F939D |
:102000008F9383E991E09F938F9381E08F930E9458 |
:10201000300C0F900F900F900F900F90E090EC030A |
:10202000F090ED030091EE031091EF0317FDC7C28E |
:102030004BE2942EC801B70120E836E948E950E0A8 |
:102040000E94222F59016A01C801B70120E836E930 |
:1020500048E950E00E94222FDC01CB01BC01CD01F8 |
:1020600020E137E240E050E00E94222FE90137FDF5 |
:10207000A2C2C801B70120E836E948E950E00E9451 |
:10208000222FDC01CB01BC01CD0120E137E240E091 |
:1020900050E00E94222FDC01CB01BC01CD012AE0DF |
:1020A00030E040E050E00E94222FDA01C90197FDA4 |
:1020B00094C284E18093E9033F932F93DF93CF93FE |
:1020C000BF92AF92892D99279F938F9386EA91E0D3 |
:1020D0009F938F9381E08F930E94300CE090F003E8 |
:1020E000F090F1030091F2031091F3038DB79EB7C6 |
:1020F0000B960FB6F8949EBF0FBE8DBF17FD6AC238 |
:102100002BE2922EC801B70120E836E948E950E0F9 |
:102110000E94222F59016A01C801B70120E836E95F |
:1021200048E950E00E94222FDC01CB01BC01CD0127 |
:1021300020E137E240E050E00E94222FE90137FD24 |
:1021400045C2C801B70120E836E948E950E00E94DD |
:10215000222FDC01CB01BC01CD0120E137E240E0C0 |
:1021600050E00E94222FDC01CB01BC01CD012AE00E |
:1021700030E040E050E00E94222FDA01C90197FDD3 |
:1021800021C288E28093E9033F932F93DF93CF939B |
:10219000BF92AF92892D99279F938F938DEB91E0FA |
:1021A0009F938F9381E08F930E94300CE090F40313 |
:1021B000F090F5030091F6031091F7038DB79EB7E9 |
:1021C0000B960FB6F8949EBF0FBE8DBF17FDF0C1E2 |
:1021D0008BE2982EC801B70128EE33E040E050E0D2 |
:1021E0000E94222F59016A01C801B70128EE33E08D |
:1021F00040E050E00E94222FDC01CB01EC0197FD72 |
:10220000D3C18CE38093E903DF93CF93BF92AF9266 |
:10221000892D99279F938F9384ED91E09F938F93BE |
:1022200081E08F930E94300C8DB79EB709960FB650 |
:10223000F8949EBF0FBE8DBFF2C180911804882311 |
:1022400009F485C18091FB0399278230910509F437 |
:10225000D2C1833091050CF411C1039709F4D5C1A3 |
:102260001092E9038091FA0399279F938F938CE64C |
:1022700092E09F938F9381E08F930E94300C0F9098 |
:102280000F900F900F900F9084E18093E90380915D |
:102290000004909101049F938F938FE792E09F93A6 |
:1022A0008F9311E01F930E94300C88E28093E90322 |
:1022B0000F900F900F900F900F90809104049091C9 |
:1022C00005049F938F9382E992E09F938F931F93CE |
:1022D0000E94300C8CE38093E9030F900F900F90D5 |
:1022E0000F900F9080910804909109049F938F9311 |
:1022F00085EA92E09F938F931F930E94300C0F907A |
:102300000F900F900F900F908AC11092EB0309CE9F |
:10231000E114F10409F043CE1092E9038CED90E052 |
:102320009F938F9311E01F930E94300C84E1809360 |
:10233000E9030F900F900F908AEE90E09F938F9398 |
:102340001F930E94300C88E28093E9030F900F9056 |
:102350000F9083E690E09F938F9381E090E09F93AE |
:102360008F93FF92EF9288EF90E09F938F931F934C |
:102370000E94300C8CE38093E9038DB79EB70996D9 |
:102380000FB6F8949EBF0FBE8DBF84E091E02CC0C5 |
:102390008093E9038FE091E09F938F9311E01F9367 |
:1023A0000E94300C84E18093E9030F900F900F900E |
:1023B0008BE191E09F938F931F930E94300C88E2F2 |
:1023C0008093E9030F900F900F9080E391E09F932B |
:1023D0008F931F930E94300C8CE38093E9030F903E |
:1023E0000F900F9085E491E09F938F931F930E942D |
:1023F000300C0F900F900F9012C1039709F0FACD97 |
:102400001092E9038091FA0399279F938F9380E8B4 |
:1024100091E0F9CD1092E90388EB92E09F938F93BE |
:10242000C1E0CF930E94300C0F900F900F908091DD |
:1024300056068823D1F584E18093E90385EC92E088 |
:102440009F938F93CF930E94300C88E28093E9038F |
:102450000F900F900F908AED92E09F938F93CF9300 |
:102460000E94300C8CE38093E9030F900F900F9043 |
:102470008FEE92E09F938F93CF93B9CF892B09F082 |
:10248000EFCE1092E9038091FA0399279F938F93DF |
:1024900083E392E0EECE1092E9038091FA0399274C |
:1024A0009F938F938DE691E0AECD84E18093E90315 |
:1024B00084E093E09F938F93CF930E94300C88E247 |
:1024C0008093E9030F900F900F9080914D069091AB |
:1024D0004E069F938F938091500699279F938F93D9 |
:1024E00080914F0699279F938F9389E193E09F9363 |
:1024F0008F93CF930E94300C8CE38093E9038DB7C8 |
:102500009EB709960FB6F8949EBF0FBE8DBF8091FF |
:102510005406909155069F938F9380915306992767 |
:102520009F938F938091520699279F938F938091C9 |
:10253000510699279F938F938EE293E09F938F93F9 |
:10254000CF930E94300C8DB79EB70B9670CE8093C0 |
:10255000E90388EE91E09F938F9311E01F930E940F |
:10256000300C84E18093E9030F900F900F9084EF7B |
:1025700091E09F938F931F930E94300C88E2809389 |
:10258000E9030F900F900F9089E092E09F938F9353 |
:102590001F930E94300C8CE38093E9030F900F90FF |
:1025A0000F908EE192E020CFD095C195DF4F29CEDC |
:1025B0009DE2992E0FCED095C195DF4F5ACD5DE2A9 |
:1025C000952E38CD309521953F4FDBCDD095C195D7 |
:1025D000DF4FB7CD3DE2932E95CD309521953F4FFE |
:1025E00068CD1092E9038091FA0399279F938F9306 |
:1025F0008AE591E008CD1092E9038091FA039927CA |
:102600009F938F9386E492E034CE1092E9038091F9 |
:10261000FA0399279F938F9389E592E02ACE10922F |
:10262000EA03DF91CF911F910F91FF90EF90DF9020 |
:10263000CF90BF90AF909F900895209A299A219AA9 |
:10264000289A08959C0160E971E00E94EC2E892B84 |
:1026500061F0C90183709070892B51F4C90164E65F |
:1026600070E00E94EC2E892B19F081E090E0089533 |
:1026700080E090E008958F929F92AF92BF92CF92A8 |
:10268000DF92EF92FF920F931F93CF93DF934C0152 |
:1026900080919104882321F08091680482FD03C019 |
:1026A000F40111864DC18091680483FFF9CF8091B8 |
:1026B0005D0490915E04A0915F04B0916004BC0140 |
:1026C000CD0128EE33E040E050E00E94002F590198 |
:1026D0006A018091650490916604AA2797FDA095F0 |
:1026E000BA2F2FE0A216B104C104D10408F01FC113 |
:1026F0000197A109B10941E75AE369E070E0A40E2E |
:10270000B51EC61ED71E7C018D0123E0EE0CFF1CFA |
:10271000001F111F2A95D1F7E81AF90A0A0B1B0BA3 |
:1027200023E837E04BE050E0E20EF31E041F151FD4 |
:10273000C601B50120E831E541E050E00E94002FDC |
:10274000E20EF31E041F151FC801B70121EB3AE387 |
:1027500042E050E00E94002F80E991E0289FE001D4 |
:10276000299FD00D389FD00D11242196C801B701A3 |
:1027700021EB3AE342E050E00E94002F7B018C0104 |
:102780002CEA3EE840E050E00E94002F84E690E012 |
:10279000289FB001299F700D389F700D1124C60F1E |
:1027A000D71FC801B7012CEA3EE840E050E00E9484 |
:1027B000002F7B018C0125EB35E040E050E00E94CA |
:1027C000002FA901440F551F440F551FC40FD51FDB |
:1027D000C801B70125EB35E040E050E00E94002F32 |
:1027E0007B018C0177E4E71674E0F70670E00707D9 |
:1027F00070E0170708F098C0C801B7012DE631E076 |
:1028000040E050E00E94002FA9019E01240F351FD7 |
:10281000D4012D933C938DE691E0489FE001499FC0 |
:10282000D00D589FD00D1124CE01AA27BB27E81A3E |
:10283000F90A0A0B1B0B0894E11CF11C011D111D68 |
:10284000C9010E942213882309F46BC04EE853E0AB |
:1028500020E0E22FFF27EE0FFF1FEE0FFF1FE40F18 |
:10286000F51F80819181A281B3818E159F05A007FC |
:10287000B10788F484819581A681B7818E159F0563 |
:10288000A007B10740F02F5FE4012A838081EE2D7D |
:10289000E81BEB832CE02F5F2C30D8F2C601B5018A |
:1028A00020E831E541E050E00E94002F5B016C011F |
:1028B00020E13EE040E050E00E94002FE4012C8344 |
:1028C000C601B50120E13EE040E050E00E94002F4B |
:1028D0005B016C012CE330E040E050E00E94002FEF |
:1028E0002D83C601B5012CE330E040E050E00E94AA |
:1028F000002F6E8380915D0490915E04A0915F042F |
:10290000B0916004BC01CD0128EE33E040E050E01E |
:102910000E94002FDC01CB0198878F8381E089879B |
:102920000FC04AE553E094CF43E050E06ECFC1EFD3 |
:10293000DFEFEFEFFFEFAC0EBD1ECE1EDF1EE3CECE |
:10294000DF91CF911F910F91FF90EF90DF90CF908B |
:10295000BF90AF909F908F90089510929104109225 |
:102960005C0410923F04109218040895CF93809154 |
:102970009204909193040E944D0F8823D9F01092F5 |
:10298000940484E690E00E94440F90939304809313 |
:10299000920480919404823008F4E5C080919104FF |
:1029A000813071F082E08093910480935C04809385 |
:1029B0003F04D9C0809194048F5F80939404E1CF49 |
:1029C00080915C04813071F7C0913F04C13051F7B0 |
:1029D0008091190490911A04019690931A0480939F |
:1029E0001904809118048130E9F210921804809142 |
:1029F00068048093F90380918C048093FA0380919A |
:102A000067048093FB038091750490917604A091F4 |
:102A10007704B09178048093FC039093FD03A09316 |
:102A2000FE03B093FF038091850490918604A091EA |
:102A30008704B09188048093140490931504A093A4 |
:102A40001604B09317048DE496E00E943B13809126 |
:102A5000440490914504A0914604B09147048093AA |
:102A6000EC039093ED03A093EE03B093EF038091FA |
:102A7000480490914904A0914A04B0914B0480937A |
:102A8000F0039093F103A093F203B093F3038091CA |
:102A9000500490915104A0915204B091530480933A |
:102AA000F4039093F503A093F603B093F703C09358 |
:102AB000F8038091230490912404A0912504B091FF |
:102AC00026048093040490930504A0930604B09315 |
:102AD000070480911F0490912004A0912104B091DB |
:102AE00022048093000490930104A0930204B09305 |
:102AF00003048091270490912804A0912904B091A7 |
:102B00002A04B095A095909581959F4FAF4FBF4FE8 |
:102B10008093080490930904A0930A04B0930B04D3 |
:102B200080912F0490913004A0913104B09132042F |
:102B300080930C0490930D04A0930E04B0930F04A3 |
:102B40008091330490913404A0913504B0913604FF |
:102B50008093100490931104A0931204B093130473 |
:102B6000C09318041FCFCF910895482F209195044A |
:102B7000822F99278430910579F18530910594F45D |
:102B80008130910509F467C0823091050CF45BC077 |
:102B90008230910509F48DC0039709F48FC010921B |
:102BA0009504DDC08630910509F45AC08630910540 |
:102BB0003CF18730910509F4A9C0089781F780910D |
:102BC0009904481709F4C1C0E0919E04F0919F0454 |
:102BD000108210929504C3C0842F99279093970474 |
:102BE0008093960490919804940F90939804809108 |
:102BF0009904890F8093990485E080939504AFC070 |
:102C0000842F9927982F8827209196043091970434 |
:102C1000820F931F90939704809396049091980449 |
:102C2000940F9093980480919904890F809399044C |
:102C3000E0919E04F0919F048081813009F46DC081 |
:102C4000108286E0DACF892B09F0A9CF453B09F045 |
:102C5000A6CF81E0D2CF423609F0A1CF82E0CDCF1E |
:102C6000E0919A04F0919B0480919C0490919D04C2 |
:102C7000E817F90708F093CF4193F0939B04E09392 |
:102C80009A0490919804940F9093980480919904D9 |
:102C9000890F8093990480919604909197040197ED |
:102CA0009093970480939604892B09F058C087E08D |
:102CB000A4CF413009F073CF83E09FCF842F9927B1 |
:102CC00086309105D1F18730910564F5029709F0BE |
:102CD00066CF80E494E090939B0480939A044D9691 |
:102CE00090939D0480939C04019790939F048093FC |
:102CF0009E042223A1F184E080939504842F8F5FAA |
:102D0000809398048F5F8093990429C080919804E0 |
:102D1000481709F059CF88E070CF109295040E94AF |
:102D2000B6141DC0429709F03ACF8BE194E090931E |
:102D30009B0480939A048596D3CF8DE594E090937D |
:102D40009B0480939A04C596CBCFE0919E04F091AA |
:102D50009F0481E080830E94B614109295048091B4 |
:102D600000049091010490938705809386058091DB |
:102D700004049091050490938905809388058091BF |
:102D800008049091090490938B0580938A058091A3 |
:102D9000FA03992790938D0580938C058091EC031D |
:102DA0009091ED0390938F0580938E058091F003B1 |
:102DB0009091F10390939105809390058091F40395 |
:102DC0009091F503909393058093920508959FB792 |
:102DD000F89411B812B88FEF80937E0080917C0038 |
:102DE0008F7180937C0080917C00807E80937C003A |
:102DF00087E080937A0080917B00887F80937B00BE |
:102E000080917A00886C80937A009FBF08951F920A |
:102E10000F920FB60F9211242F933F938F939F938E |
:102E20008091A104282F33278F5F2330310509F4C7 |
:102E300041C02430310544F52130310509F46CC01E |
:102E4000223031050CF44EC08093A10480917800AB |
:102E50009091790090935A068093590683E080936D |
:102E6000A00480917C00807E9091A004892B8093A7 |
:102E70007C008091A104882309F46FC080917A00BE |
:102E8000886C80937A0069C025303105C9F12530FE |
:102E90003105E4F02630310509F44AC02730310508 |
:102EA00009F452C01092A0041092A10481E0809312 |
:102EB000C203D7CF8093A1048091780090917900CC |
:102EC00090935C0680935B0684E0C9CF8093A10455 |
:102ED00080917800909179009093620680936106CA |
:102EE00085E0BDCF232BF1F68093A104809178007B |
:102EF00090917900909366068093650681E0AFCF4C |
:102F00008093A10480917800909179009093580665 |
:102F10008093570686E0A3CF8093A1048091780028 |
:102F200090917900909364068093630682E097CF36 |
:102F30008093A104809178009091790090935E062F |
:102F400080935D0687E08BCF809178009091790027 |
:102F50009093600680935F06A5CF9F918F913F91DC |
:102F60002F910F900FBE0F901F901895469A3E9884 |
:102F700082E390E00E94440F9093A3048093A20404 |
:102F80000895CF93C0E08091A2049091A3040E9481 |
:102F90004D0F882301F1369B0BC0C093A40482E33C |
:102FA00090E00E94440F9093A3048093A20413C066 |
:102FB0008091A4048F5F8093A404813051F08A3003 |
:102FC00041F08091A4048A3051F787E08093A404F3 |
:102FD000E6CFC1E0F6CF8C2F9927CF910895CF92FD |
:102FE000DF92EF92FF920F931F93CF93DF938C01A9 |
:102FF0006A017B0180E090E040E050E060E070E03A |
:103000004C155D056E057F05D8F4382F2227E92F72 |
:10301000FF27D8018D918D019927E827F927EE0F19 |
:10302000FF1FE85BFC4F80819181822793274F5FD0 |
:103030005F4F6F4F7F4F4C155D056E057F0528F381 |
:10304000DF91CF911F910F91FF90EF90DF90CF9084 |
:1030500008952C9A089584B1806B84B926980E94B3 |
:10306000291882E58CBD8DB581608DBD2A9A229884 |
:1030700008950E9429181CBC1DBC08958FEF8EBDB9 |
:103080000DB407FEFDCF8EB5992708958EBD0DB402 |
:1030900007FEFDCF08952C980895EF92FF920F93AD |
:1030A0001F93CF93DF93EC017A018B0140E0542F03 |
:1030B000E114F1040105110509F169E0FE01E50FD4 |
:1030C000F11DE081F7E0440F2E2F33272078307078 |
:1030D000842F9927807890702817390709F04627A0 |
:1030E000EE0FF150F7FFEFCF5F5F852F9927AA27EB |
:1030F000BB278E159F05A007B10700F3842F9927E2 |
:10310000880F991F482F4160842F9927DF91CF9115 |
:103110001F910F91FF90EF9008950F931F93CF93FE |
:10312000DF938C010E944B18C8010E94440FEC01F0 |
:103130000E943E18082FCE010E944D0F882311F4E3 |
:103140000F3FB1F7802F9927DF91CF911F910F91FA |
:103150000895CF92DF92EF92FF920F931F93CF9338 |
:10316000DF93CDB7DEB726970FB6F894DEBF0FBE5C |
:10317000CDBF9A01AB01EE24FF2480648983852FA3 |
:103180009927AA27BB278A834B833C832D8345E05D |
:1031900050E060E070E0CE0101960E944D188E83F1 |
:1031A0000E9429188FEF0E9446180E944B1884EF46 |
:1031B00091E00E948D188E010F5F1F4F36E0C32EE5 |
:1031C000D12CCC0EDD1EF80181918F010E94461892 |
:1031D0008AE090E00197F1F7C016D106A0F70E94AF |
:1031E0003E18282FC7010894E11CF11C855F91400F |
:1031F00010F42F3FA1F3822F992726960FB6F8944B |
:10320000DEBF0FBECDBFDF91CF911F910F91FF9019 |
:10321000EF90DF90CF900895EF92FF920F931F935E |
:10322000CF93C82F7A018B0140E050E060E070E05E |
:1032300087E30E94A91888232CF0B801A7018C2FDE |
:103240000E94A9189927CF911F910F91FF90EF909D |
:103250000895AF92BF92CF92DF92EF92FF920F93B9 |
:103260001F93CF93DF935901670178010E94A9183A |
:10327000982F8823C9F024E0892F99279F938F9353 |
:10328000C22FDD27DF93CF9388E495E09F938F9340 |
:103290001F920E94300C8DB79EB707960FB6F89418 |
:1032A0009EBF0FBE8DBF26C00E943E18982F807F04 |
:1032B000F9F09E3FC9F7C0E0D0E0C114D104E104A9 |
:1032C000F10479F085010E943E18F80181938F0185 |
:1032D0002196CE01AA27BB278C159D05AE05BF05FB |
:1032E00090F30E943E180E943E18C0E0D0E002C059 |
:1032F0002AE0C2CFCE01DF91CF911F910F91FF90B5 |
:10330000EF90DF90CF90BF90AF900895AF92BF92B3 |
:10331000CF92DF92EF92FF920F931F93CF93DF93A1 |
:10332000CDB7DEB726970FB6F894DEBF0FBECDBF80 |
:103330006C01DC018C9199279F938F938AE795E02C |
:103340009F938F931F920E94300CF601818192818E |
:103350009A8389831B820F900F900F900F900F908C |
:103360008E010F5F1F4F1F930F9384E995E09F938A |
:103370008F931F920E94300C83E090E0C80ED91EFC |
:1033800085E0D801F60101900D928A95E1F78DEF65 |
:103390009FEFC80ED91E1E820F900F900F900F90B6 |
:1033A0000F901F930F938BEA95E09F938F931F923B |
:1033B0000E94300C0F900F900F900F900F90F6011D |
:1033C000808599278F7090709F938F9380858295C9 |
:1033D0008F7099279F938F9380EC95E09F938F93A5 |
:1033E0001F920E94300C8DB79EB707960FB6F894C7 |
:1033F0009EBF0FBE8DBF88ED95E09F938F931F9268 |
:103400000E94300C0F900F900F90F7EEAF2EF5E06A |
:10341000BF2EE9E0EE2EF12CEC0CFD1C03E010E0D9 |
:10342000D7018D917D0199279F938F93BF92AF9282 |
:103430001F920E94300C0F900F900F900F900F90E2 |
:103440000150104017FFECCF8CEE95E09F938F93C7 |
:103450001F920E94300CF6018685482F55274F7029 |
:10346000507082958F70282F3327858599278295F4 |
:103470009295907F9827807F9827282B392B20536F |
:10348000384F0F900F900F903F932F935F934F9370 |
:103490008FEE95E09F938F931F920E94300C8DB713 |
:1034A0009EB707960FB6F8949EBF0FBE8DBF2696A7 |
:1034B0000FB6F894DEBF0FBECDBFDF91CF911F9145 |
:1034C0000F91FF90EF90DF90CF90BF90AF90089555 |
:1034D000EF92FF920F931F93A0E1EA2EF12C012DA2 |
:1034E000112D9C0140E050E060E070E08AE00E9415 |
:1034F000291999271F910F91FF90EF900895EF924E |
:10350000FF920F931F93B0E1EB2EF12C012D112DA3 |
:103510009C0140E050E060E070E089E00E942919E1 |
:1035200099271F910F91FF90EF900895EF92FF92CE |
:103530000F931F93CF93DF93CDB7DEB726970FB6C8 |
:10354000F894DEBF0FBECDBF1A9982C08AE096E024 |
:103550009F938F931F920E94300C0E942B180F9004 |
:103560000F900F9089E196E09F938F931F920E9496 |
:10357000300C0F900F900F908CE196E09F938F93FB |
:103580001F920E94300C109267060E9429180F901B |
:103590000F900F900EE010E08FEF0E944618015040 |
:1035A000104017FFF9CF00E010E040E050E060E08D |
:1035B00070E080E00E94A918F82E8983C8010F5F8F |
:1035C0001F4F855F914008F053C081E0F81669F7FE |
:1035D0004AEA51E060E070E088E00E94A91889831F |
:1035E00087FD25C182FF4DC081E08093680640E0E1 |
:1035F00050E060E070E08AE30E94A918898387FDAB |
:1036000025C182FDF9C08E010E5F1F4FB5E0EB2E84 |
:10361000F12CEC0EFD1E0E943E18F80181938F01E3 |
:10362000EE16FF06C0F78B81992780739070C097C4 |
:1036300009F448C08BEA96E09F938F931F920E94F3 |
:10364000300C15E00F900F900F900E942918ADC11B |
:103650000E9439181092670611E087E897E09F935F |
:103660008F931F920E94300C0F900F900F909DC16E |
:103670008BE296E09F938F931F920E94300C12E092 |
:10368000E1CF82E0809368068E010E5F1F4F85E0D8 |
:10369000E82EF12CEC0EFD1E0E943E18F8018193DD |
:1036A0008F01EE16FF06C0F78D818A3A09F092C0AD |
:1036B0008C8199278F709070019709F498CF88E6D4 |
:1036C00096E0BACF80ED97E00E94440F8C0140E075 |
:1036D00050E060E070E089E20E940C198983882341 |
:1036E0000CF493C0C8010E944D0F282F882309F0C5 |
:1036F00098C0F980F0FCEBCFFF2009F074C040E0E7 |
:1037000052E060E070E080E10E94A918082F882351 |
:1037100009F093C08DE696E00E94681A182F88235E |
:1037200009F09AC08DE796E00E947F1A182F88232F |
:1037300009F0F7C082E497E09F938F931F930E9454 |
:10374000300C0F900F900F9080917D06829586959A |
:10375000869583709927009709F486C0019709F42C |
:10376000F3C01092690610926A0610926B061092CE |
:103770006C068091680699278130910509F4D9C0BB |
:10378000029709F40FC18091690690916A06A09191 |
:103790006B06B0916C0604E1B695A7959795879551 |
:1037A0000A95D1F79F938F9382E797E09F938F932A |
:1037B0001F920E94300C8DE696E00E94861981E0EF |
:1037C000809367060F900F900F900F900F900E94BC |
:1037D0002918EBC08BE496E09F938F931F920E9471 |
:1037E000300C14E02FCF8BEE96E09F938F932F93A6 |
:1037F0000E94300C13E026CF8BE996E09F938F93C5 |
:103800001F920E94300C16E01DCF99279F938F9333 |
:1038100089EC96E09F938F931F920E94300C14E0E6 |
:10382000D1CF8DED96E09F938F931F92E1CF992793 |
:103830009F938F9389E396E0EDCF87EF96E09F9378 |
:103840008F93FF920E94300C1BE0FCCE99279F9330 |
:103850008F9388E896E0DECF8AE197E09F938F937D |
:10386000FF920E94300CEECE309182063F70809124 |
:103870008306482F5527662777274370507060705E |
:103880007070AAE0440F551F661F771FAA95D1F7E5 |
:10389000809184069927AA27BB27880F991FAA1F02 |
:1038A000BB1F880F991FAA1FBB1F482B592B6A2BC0 |
:1038B0007B2B8091850682958695869583709927C6 |
:1038C000AA27BB27482B592B6A2B7B2B20918606D6 |
:1038D0002370220F809187069927880F892F881FD0 |
:1038E000990B9195282B4F5F5F4F6F4F7F4F822F22 |
:1038F0009927029604C0440F551F661F771F8A95AB |
:10390000D2F704C0440F551F661F771F3A95D2F7B0 |
:103910004093690650936A0660936B0670936C0639 |
:1039200028CF8FE297E09F938F930F930E94300CE4 |
:1039300089CE87E497E09F938F931F920E94300C6B |
:103940000F900F900F901FCF809184069927AA2780 |
:10395000BB278F739070A070B070BC0155274427AF |
:10396000809185069927AA27BB27BA2FA92F982FC0 |
:103970008827482B592B6A2B7B2B80918606992709 |
:10398000AA27BB27482B592B6A2B7B2B83E1440F9B |
:10399000551F661F771F8A95D1F740505040684FDA |
:1039A0007F4FB6CF88E597E0C6CF812F992726961F |
:1039B0000FB6F894DEBF0FBECDBFDF91CF911F9140 |
:1039C0000F91FF90EF90089588E997E09F938F9370 |
:1039D0001F920E94300C0E943918109267061092B4 |
:1039E000690610926A0610926B0610926C0610928D |
:1039F00068060F900F900F9089EA97E09F938F933E |
:103A00001F920E94300C0F900F900F9080E090E07A |
:103A10000895CF92DF92EF92FF920F931F93CF936F |
:103A2000DF93DC01CB018A01EE24FF2429E0880F1B |
:103A3000991FAA1FBB1F2A95D1F7AC01BD0188E1D0 |
:103A40000E94A918C82F882309F040C0C3E1D0E024 |
:103A50000E943E182197D7FFFBCF40E052E060E084 |
:103A600070E0C8010E94EF176C018EEF0E944618AB |
:103A7000E80101501E4F89910E9446180C171D073E |
:103A8000D0F78D2D99270E9446188C2D0E9446183C |
:103A90000E943E18C82FC7010894E11CF11C855FE5 |
:103AA0009140A0F52C2F3327C901817190700197A7 |
:103AB00079F7C9018F7190708B30910569F18C3065 |
:103AC000910534F50597D9F01CE001C014E08C2F66 |
:103AD00099279F938F93C12FDD27DF93CF938CEA94 |
:103AE00097E09F938F931F920E94300C8DB79EB7E3 |
:103AF00007960FB6F8949EBF0FBE8DBF27C080ED0E |
:103B000097E00E948D18C82F8F3F41F017E0DFCF5C |
:103B10004D97D1F619E0DBCF18E0D9CF40E050E067 |
:103B200060E070E08DE00E94A918C82F882341F45E |
:103B30000E943E18C82F882339F4C0E0D0E006C0A8 |
:103B400014E00E943E18C3CF19E0FBCFCE01DF91F5 |
:103B5000CF911F910F91FF90EF90DF90CF9008953C |
:103B6000EF92FF920F931F93DC01CB019A0159E072 |
:103B7000880F991FAA1FBB1F5A95D1F7E12C42E06D |
:103B8000F42E012D112DAC01BD0181E10E942919F6 |
:103B900099271F910F91FF90EF900895FC01892BB9 |
:103BA00019F08185882329F420E030E040E050E0DE |
:103BB00063C0808191818C5B97409C01442755278D |
:103BC000A9E1220F331F441F551FAA95D1F7828107 |
:103BD0009927AA27BB278F709070A070B07075E1ED |
:103BE000880F991FAA1FBB1F7A95D1F7282B392B55 |
:103BF0004A2B5B2B83819927AA27BB278F71907053 |
:103C0000A070B070DC0199278827282B392B4A2B0C |
:103C10005B2B84819927AA27BB278F719070A07096 |
:103C2000B0707BE0880F991FAA1FBB1F7A95D1F750 |
:103C3000282B392B4A2B5B2B85819927AA27BB2759 |
:103C40008F739070A070B07065E0880F991FAA1FE5 |
:103C5000BB1F6A95D1F7282B392B4A2B5B2B86810A |
:103C600086959927AA27BB278F719070A070B07096 |
:103C7000282B392B4A2B5B2BCA01B9010895A0E0F0 |
:103C8000B0E060E0AD019A012C55394FF901E15ED9 |
:103C9000FD4F8081882331F06F5F405E5D4F633060 |
:103CA00090F303C0D90181E08083CD01089500978E |
:103CB00071F020E0E4EAF6E0E817F90749F02F5F39 |
:103CC000E05EFD4F2330C0F380E090E0089508955A |
:103CD000E15EFD4F1082EF51F2401082118212829C |
:103CE000138214821582168217821086128611861C |
:103CF00013861486158616861786108A118A128AEC |
:103D0000138AE55EFD4F1082118212821382EB51FD |
:103D1000F240148A158A168A178A118E108E128E16 |
:103D200081E090E00895CF93DF93DC01EB0140E068 |
:103D300050E030E08C918F3239F442C08F3251F034 |
:103D40003F5F4115510559F4FD01E30FF11D8081DD |
:103D50008823A1F7AF013F5F41155105A9F31B86E9 |
:103D600080E2FE012AE08193215027FFFCCF30E062 |
:103D7000232F8C918F3279F1E32FFF27CF018A0F08 |
:103D80009B1F84179507D0F42B3038F5EA0FFB1FE3 |
:103D900090819E32D9F0892F81568A3198F4FE01A4 |
:103DA000E20FF11D905290833F5F2F5FE32FFF27BB |
:103DB000CF018A0F9B1F8417950730F3CA010FC0EC |
:103DC00031E0C2CFFE01E20FF11DEDCF293028F422 |
:103DD0003F5F28E0D1CF31E0CFCF80E090E0DF91AE |
:103DE000CF9108959C0180918D06882329F420E0CD |
:103DF00030E040E050E021C02230310510F422E0F4 |
:103E000030E02250304080918E069927289FA001F3 |
:103E1000299F500D389F500D1124CA019C01442741 |
:103E2000552780919C0690919D06A0919E06B09189 |
:103E30009F06280F391F4A1F5B1FCA01B901089549 |
:103E4000CF93DF93FC01EB0180918D06882319F459 |
:103E500080E090E019C080919C0690919D06A09111 |
:103E60009E06B0919F06C81BD90BEA0BFB0B8091F5 |
:103E70008E06282F332744275527BE01CF010E94E5 |
:103E8000002FDA01C9010296DF91CF910895809148 |
:103E90008D06992708955F926F927F928F929F92DD |
:103EA000AF92BF92CF92DF92EF92FF920F931F9348 |
:103EB000CF93DF93CDB7DEB7C050D2400FB6F894A2 |
:103EC000DEBF0FBECDBF9C016624772443018091E5 |
:103ED0008D06882309F47FC044275527220F331FFE |
:103EE000441F551FDA01C90169E0B695A79597955A |
:103EF00087956A95D1F7E0909406F0909506009129 |
:103F0000960610919706E80EF91E0A1F1B1F9FEFD9 |
:103F1000A92E91E0B92EC12CD12CA222B322C42209 |
:103F2000D5223BC081E0582E1182108244275527AC |
:103F3000220F331F441F551FDA01C90119E0B6953E |
:103F4000A795979587951A95D1F7E0909406F090EC |
:103F500095060091960610919706E80EF91E0A1F25 |
:103F60001B1FBEEFAB2EB1E0BB2EC12CD12CA22269 |
:103F7000B322C422D522E614F7040805190511F46A |
:103F8000552049F4AE014F5F5F4FC401B3010E9459 |
:103F9000091D882301F5552009F1E614F7040805E9 |
:103FA000190559F037014801AE014F5F5F4FC80155 |
:103FB000B7010E94B01D882371F4FE013196EA0D0D |
:103FC000FB1D20813181C90102978E5E9F4F08F44D |
:103FD000A9CF5524A9CF80E090E002C081E090E015 |
:103FE000C050DE4F0FB6F894DEBF0FBECDBFDF91DD |
:103FF000CF911F910F91FF90EF90DF90CF90BF90E6 |
:10400000AF909F908F907F906F905F900895CF9228 |
:10401000DF92EF92FF920F931F93CF93DF93EC0108 |
:10402000892B09F474C080918D06882309F46FC030 |
:104030008B8599278136910521F08737910509F005 |
:1040400066C089859A85892B09F049C0ABE1EA2EC3 |
:10405000F12CEC0EFD1EFBE1CF2EF2E0DF2ECC0E9C |
:10406000DD1E8C899D89AE89BF89F601808391838D |
:10407000A283B383A701BC01CD010E94B01D882398 |
:1040800009F043C0088D198D000F111F029512957C |
:10409000107F1027007F10270E0D1F1D8C859D851A |
:1040A000AE85BF85F801848F958FA68FB78F8DE47D |
:1040B00096E00E94CE1DDC01CB01F801868B978B28 |
:1040C000A08FB18FA701F6016081718182819381F8 |
:1040D0000E94091D8823C9F480E090E01AC01BE10A |
:1040E000E12EF12CEC0EFD1EBBE1CB2EB2E0DB2E5F |
:1040F000CC0EDD1EA701F601608171818281938162 |
:104100000E94091D882309F4ACCF0E94A7208FEFDD |
:104110009FEFDF91CF911F910F91FF90EF90DF9074 |
:10412000CF9008950F931F93CF93DF93EC010FEF80 |
:104130001FEF009731F00E9407208C01CE010E94F2 |
:10414000571EC801DF91CF911F910F910895EF92F3 |
:10415000FF921F93CF93DF93EE24FF24C4EAD6E0AF |
:1041600012E0C15EDD4F8881CF51D240813031F005 |
:104170001150C05EDD4F17FFF4CF06C0CE010E9484 |
:104180009220E80EF91EF4CF0E94E41C10928D06D6 |
:104190008E2D9927DF91CF911F91FF90EF90089579 |
:1041A0002F923F924F925F926F927F928F929F9247 |
:1041B000AF92BF92CF92DF92EF92FF920F931F9335 |
:1041C000CF93DF93CDB7DEB724970FB6F894DEBF59 |
:1041D0000FBECDBFAC011A82198280918D06882353 |
:1041E00009F488C04115510509F484C08824992434 |
:1041F00054019BE1292E92E0392E240E351E8A01AE |
:10420000055E1F4F2401350180919406909195061B |
:10421000A0919606B0919706880D991DAA1DBB1D09 |
:10422000F10180839183A283B383A801BC01CD01F6 |
:104230000E94B01D882309F05BC01C821B827301A1 |
:104240006201F694E794D794C79409C0AB81BC810E |
:104250001196BC83AB83AF3FB10509F058F5EB81F4 |
:10426000FC81EE0FFF1FE00FF11F80819181892BF0 |
:1042700069F78FEF9FEF91838083A801D1016D9142 |
:104280007D918D919C910E94091D882389F5EB8178 |
:10429000FC81EC0DFD1DFA83E98380E091E09C83B5 |
:1042A0008B83AB81BC811196BC83AB83AF3FB105DF |
:1042B000B1F2A8F20894811C911CA11CB11C80E0F1 |
:1042C00092E0A0E0B0E0480E591E6A1E7B1E80916D |
:1042D000920690919306AA27BB2788169906AA06EC |
:1042E000BB0640F4A981BA81AB2B09F48DCF02C083 |
:1042F0000E94A72089819A8124960FB6F894DEBF88 |
:104300000FBECDBFDF91CF911F910F91FF90EF9026 |
:10431000DF90CF90BF90AF909F908F907F906F90E5 |
:104320005F904F903F902F900895AF92BF92CF92A1 |
:10433000DF92EF92FF920F931F93CF93DF938C0145 |
:10434000EE24FF2480918D06882309F489C001158D |
:10435000110509F485C080919C0690919D06A0915D |
:104360009E06B0919F06F8012481358146815781D0 |
:10437000281739074A075B0708F472C08091A00626 |
:104380009091A106A091A206B091A306821793076F |
:10439000A407B50708F464C0CA01B9010E94201F30 |
:1043A0007C01C701AA27BB27880F991FAA1FBB1F23 |
:1043B0009C01AD0169E056954795379527956A951B |
:1043C000D1F7C0919406D0919506E0919606F091B0 |
:1043D0009706C20FD31FE41FF51F2FEFA22E21E077 |
:1043E000B22EC12CD12CA822B922CA22DB229801DC |
:1043F000255E3D4FD9014D915D916D917C91139753 |
:104400004C175D076E077F0771F0CD93DD93ED9339 |
:10441000FC931397C8014B96AC01BE01CF010E94DB |
:10442000B01D882369F4F801EA0DFB1DE38CF48CC0 |
:10443000B8EFEB16BFEFFB0630F0EE24FF2410C000 |
:104440000E94A7200DC0C7010E94F21EDC01CB0113 |
:10445000F80184839583A683B7831086128611861C |
:10446000C701DF91CF911F910F91FF90EF90DF90E7 |
:10447000CF90BF90AF9008952F923F924F925F924E |
:104480006F927F928F929F92AF92BF92CF92DF9264 |
:10449000EF92FF920F931F93CF93DF93CDB7DEB7C9 |
:1044A0002E970FB6F894DEBF0FBECDBF9A838983D7 |
:1044B0006B834C8319011A86198600E080918D0662 |
:1044C000882309F4B0C0232B09F4ADC029813A81B7 |
:1044D000232B09F4A8C0D1011D921D921D921C929C |
:1044E0001397F101C488D588E688F788C114D104F0 |
:1044F000E104F10409F09AC0809190069091910630 |
:10450000880F991F82959295907F9827807F982792 |
:10451000892F992786959C01442755272D833E8313 |
:104520004F8358878091980690919906A0919A069A |
:10453000B0919B0680839183A283B383F101808134 |
:104540009181A281B3810097A105B10509F40FC142 |
:104550009101255E3D4F3C872B871BE1812E912CDD |
:10456000820C931C9C01AD01F10184839583A68389 |
:10457000B7831086128611864424552432015901CE |
:104580006A01A40CB51CC61CD71CAB85BC85AD92BA |
:10459000BD92CD92DC921397A401C601B5010E9491 |
:1045A000B01D8823F1F5EE24FF24A701440F551F09 |
:1045B00042955295507F5427407F5427FA01E80DC9 |
:1045C000F91DFE87ED872081822F9927009751F0F2 |
:1045D000853E910539F08385FC818F233B818317CC |
:1045E00009F457C00894E11CF11CF0E1EF16F10446 |
:1045F000E0F20894411C511C611C711C2D813E810C |
:104600004F815885421653066406750608F0E7C0C8 |
:10461000002309F0E4C0F1012481358146815781EE |
:10462000AECF0E94A72080E090E00FC18091980655 |
:1046300090919906A0919A06B0919B06C816D9064A |
:10464000EA06FB0608F470C080919C0690919D06D6 |
:10465000A0919E06B0919F06C816D906EA06FB06F1 |
:1046600008F06AC08091900690919106880F991F7A |
:1046700082959295907F9827807F9827892F9927F8 |
:1046800086959C01442755272D833E834F83588769 |
:1046900055CF30E0A981BA818C91281709F0A2CFBB |
:1046A0003F5F3B3078F4632F7727AD85BE85A60F3B |
:1046B000B71FE981FA816E0F7F1F9C91DB018C91FE |
:1046C000981771F33A3008F48DCF8A01080D191D3F |
:1046D000F8018385F101828FF801828D938D0E940C |
:1046E000F21EDC01CB01F10180839183A283B383AD |
:1046F00084839583A683B783108612861186A48A45 |
:10470000B58AC68AD78AF18EE08EF801848D958DA0 |
:10471000A68DB78DF10184879587A687B78701E0BD |
:10472000E0E1EE2EF12C5ECF80919C0690919D06EB |
:10473000A0919E06B0919F06C816D906EA06FB0610 |
:1047400008F471CF8091A0069091A106A091A206D5 |
:10475000B091A3068C159D05AE05BF0508F463CF87 |
:1047600080918E06282F3327442755278DCF91011E |
:10477000255E3D4F3C872B87D901CD92DD92ED928E |
:10478000FC9213979BE1892E912C820C931CA4011F |
:10479000C701B6010E94B01D882309F042CFF10184 |
:1047A000A08DB18DAA0FBB1FA295B295B07FBA277D |
:1047B000A07FBA27A80DB91D8C919927009709F4FD |
:1047C00032CF853E910509F42ECFFD0183859927CF |
:1047D0009C012071307084FD2BC0C90136C00023BC |
:1047E00091F480919C0690919D06A0919E06B091B7 |
:1047F0009F06F101248135814681578128173907A9 |
:104800004A075B0760F429853A85232B71F0002362 |
:1048100061F4F1012481358146815781ADCEC1011A |
:104820000E9495219A878987EECF802F99270DC006 |
:10483000FD01828D938D0E94F21EDC01CB01F101FE |
:1048400080839183A283B3838DCE2E960FB6F89486 |
:10485000DEBF0FBECDBFDF91CF911F910F91FF90B3 |
:10486000EF90DF90CF90BF90AF909F908F907F9010 |
:104870006F905F904F903F902F900895BF92CF928E |
:10488000DF92EF92FF920F931F93CF93DF93CDB7F9 |
:10489000DEB72C970FB6F894DEBF0FBECDBFFC017C |
:1048A000B62EC42E790100E010E0DD24892B39F00A |
:1048B0002115310521F080918D06882319F480E0BF |
:1048C00090E033C0DF01F901148A158A168A178A2D |
:1048D000118F008F8C918823C1F48D2D992725C0CD |
:1048E0006B2D4C2D9701CE0101960E943C2288230E |
:1048F000A1F3F8018081882311F461E0D62ED8015C |
:104900008C91882351F3DD2041F7BE016F5F7F4F0B |
:10491000CD010E94931E8C010097F9F2FC01808169 |
:104920008823F1F260E148E1DDCF2C960FB6F894D0 |
:10493000DEBF0FBECDBFDF91CF911F910F91FF90D2 |
:10494000EF90DF90CF90BF900895AF92BF92CF923B |
:10495000DF92EF92FF920F931F93CF93DF93EC01BF |
:10496000EE24FF248701E1E0CE2ED12C80918D062C |
:10497000882309F45DC0209709F45AC02115310538 |
:1049800041F02230310509F465C0E888F9880A89C8 |
:104990001B89E40EF51E061F171F17FD4CC08C85E2 |
:1049A0009D85AE85BF858E159F05A007B1070CF4C8 |
:1049B00042C088819981AA81BB818C839D83AE830B |
:1049C000BF8318861A861986188A198A1A8A1B8A3A |
:1049D00080E090E0A0E0B0E08E159F05A007B10751 |
:1049E00070F5A12C72E0B72E12C01A861986888540 |
:1049F0008F5F888790918E06891788F588899989B5 |
:104A0000AA89BB898E159F05A007B107C0F401963E |
:104A1000A11DB11D888B998BAA8BBB8B89859A852B |
:104A2000019622E08030920700F79A878987E6CFC7 |
:104A300080E090E021C088899989AA89BB898E1578 |
:104A40009F05A007B10711F0C60116C0CC24DD24D4 |
:104A5000C60112C0EC84FD840E851F859ACFCE015D |
:104A60000E949521892B11F01886C8CF8885815026 |
:104A70008887BA86A986C2CFDF91CF911F910F9107 |
:104A8000FF90EF90DF90CF90BF90AF9008956F921E |
:104A90007F928F929F92AF92BF92CF92DF92EF92CE |
:104AA000FF920F931F93CF93DF938C01EE24FF248B |
:104AB00080918D06882349F00115110531F0C80158 |
:104AC0000E94D0207C01892B11F4C7017FC022E015 |
:104AD00030E040E050E060E070E0C8010E94A524B2 |
:104AE000F80164817581868197810E94201FAA2721 |
:104AF000BB27880F991FAA1FBB1F9C01AD0169E04E |
:104B000056954795379527956A95D1F7C0919406A4 |
:104B1000D0919506E0919606F0919706C20FD31FAB |
:104B2000E41FF51F2FEF622E21E0722E812C912CB5 |
:104B3000682279228A229B22BBE1AB2EB2E0BB2EF7 |
:104B4000A00EB11ED5012D913D914D915C91139711 |
:104B50002C173D074E075F0779F1CD93DD93ED9359 |
:104B6000FC931397ABE1CA2ED12CC00ED11EA60127 |
:104B7000BE01CF010E94B01D882321F5F801E60D8A |
:104B8000F71DF48EE38EA601F501608171818281AB |
:104B900093810E94091DC82F8823A1F4C7010E9498 |
:104BA000F21EDC01CB01F80184839583A683B783D1 |
:104BB000C0871286118689CFFBE1CF2ED12CC00E83 |
:104BC000D11EDCCF0E94A72080E090E0DF91CF9142 |
:104BD0001F910F91FF90EF90DF90CF90BF90AF901B |
:104BE0009F908F907F906F9008959F92AF92BF9209 |
:104BF000CF92DF92EF92FF920F931F93CF93DF93A9 |
:104C0000EC01A1E09A2E80918D06882309F452C010 |
:104C1000209709F44FC0EE24FF248701FE01EE0D1A |
:104C2000FF1D138E0894E11CF11C011D111D80E075 |
:104C3000E81682E0F80680E0080780E0180770F3C5 |
:104C4000EE24FF24870180918E068823B1F1FBE1D9 |
:104C5000AF2EF2E0BF2EAC0EBD1EEBE1CE2ED12C5E |
:104C6000CC0EDD1E0FC00894E11CF11C011D111DAE |
:104C700080918E069927AA27BB27E816F9060A070E |
:104C80001B07D8F48C819D81AE81BF818E0D9F1D45 |
:104C9000A01FB11FF50180839183A283B383A60176 |
:104CA000BC01CD010E94091D8823E9F20E94A720C2 |
:104CB0009924D9CF80E090E002C0892D9927DF9117 |
:104CC000CF911F910F91FF90EF90DF90CF90BF9009 |
:104CD000AF909F9008952F923F924F925F926F9264 |
:104CE0007F928F929F92AF92BF92CF92DF92EF927C |
:104CF000FF920F931F93CF93DF93CDB7DEB76197EA |
:104D00000FB6F894DEBF0FBECDBF9A838983362ECF |
:104D10008A01CC24DD24DC86CB86DE86CD86222467 |
:104D200080918D06882309F480C04115510509F44E |
:104D30007CC029813A81232B09F477C0DA011D92C6 |
:104D40001D921D921C921397FA01A488B588C688FB |
:104D5000D788A114B104C104D10409F069C08091BD |
:104D6000900690919106880F991F82959295907F59 |
:104D70009827807F9827892F992786959C0144271B |
:104D800055272F83388749875A874D855E855C87ED |
:104D90004B878091980690919906A0919A06B091C0 |
:104DA0009B0680839183A283B383F80180819181E4 |
:104DB000A281B3810097A105B10509F07BC0055E12 |
:104DC0001D4FD801AD92BD92CD92DC9213970B513D |
:104DD0001240FBE1EF2EF12CE00EF11EA701C601FF |
:104DE000B5010E94B01D8823F1F4F801A08DB18DAA |
:104DF000AA0FBB1FA295B295B07FBA27A07FBA2792 |
:104E0000AE0DBF1D8C919927009779F0853E9105D5 |
:104E100061F0FD01838599279C012071307084FD2C |
:104E20003AC0C901B1C10E94A72080E090E0ACC1A6 |
:104E30008091980690919906A0919A06B0919B0650 |
:104E4000A816B906CA06DB0608F454C180919C0670 |
:104E500090919D06A0919E06B0919F06A816B90656 |
:104E6000CA06DB0608F04EC180919006909191062B |
:104E7000880F991F82959295907F9827807F982719 |
:104E8000892F992786959C01442755272F8338879A |
:104E900049875A878ACFFD01A28DB38DBC87AB8726 |
:104EA000CD010E94F21EDC01CB01F80180839183C9 |
:104EB000A283B383C8010E94D0203C01892B09F44E |
:104EC00016C1D8012D913D914D915C91F80124833B |
:104ED00035834683578310867BE1472E72E0572E39 |
:104EE000400E511E6BE1E62EF12CE00EF11E1B82EE |
:104EF0001C821D821E828B809C80AD80BE80820EB3 |
:104F0000931EA41EB51ED2018D929D92AD92BC92AD |
:104F10001397A701C501B4010E94B01D882309F0B1 |
:104F200082CFCC24DD240AC0853EB1F00894C11C98 |
:104F3000D11CB0E1CB16D10408F068C0F601EE0F29 |
:104F4000FF1FE295F295F07FFE27E07FFE27EE0D32 |
:104F5000FF1D8081882341F7F98BE88BDF012981D0 |
:104F60003A819AE0F90181919F018D93915097FFC9 |
:104F7000F9CFE889F9893386738E628E8DE496E0E5 |
:104F80000E94CE1DDC01CB01E889F989868B978BC5 |
:104F9000A08FB18F148E158E168E178EA701D20199 |
:104FA0006D917D918D919C910E94091D8F87882321 |
:104FB00009F09BC0C3010E94F21EDC01CB01F80185 |
:104FC00080839183A283B38384839583A683B783ED |
:104FD0002F85208712861186328E148615861686B6 |
:104FE0001786848A958AA68AB78AD18EC08E34FCA9 |
:104FF0002CC081E0282EB0E1CB2ED12C0894C11C0E |
:10500000D11CB0E1CB16D10408F498CF2B813C81A0 |
:105010004D815E812F5F3F4F4F4F5F4F2B833C830E |
:105020004D835E838F819885A985BA8528173907B6 |
:105030004A075B0708F081C0222009F07EC0F80112 |
:10504000248135814681578156CFC8010E94F525BC |
:10505000F80180819181A281B381F20180839183E3 |
:10506000A283B383A701BC01CD010E94B01D882398 |
:10507000E1F52EE2F801238F20E2F701319699E065 |
:105080002193915097FFFCCF80E1F7018387738EC6 |
:10509000628E168A178A108E118E148E158E168EB9 |
:1050A000178E3EE230A331A3822FB29698E081930F |
:1050B000915097FFFCCF80E1F70183A72B853C85BA |
:1050C00033AF22AF16AA17AA10AE11AE14AE15AEAA |
:1050D00016AE17AEA701D2016D917D918D919C9175 |
:1050E0000E94091D882309F484CF0E94A720822DE5 |
:1050F00099274AC080919C0690919D06A0919E069A |
:10510000B0919F06A816B906CA06DB0608F48DCE34 |
:105110008091A0069091A106A091A206B091A3064D |
:105120008A159B05AC05BD0508F47FCE80918E06DF |
:10513000282F332744275527A9CE2220B9F4809160 |
:105140009C0690919D06A0919E06B0919F06F80145 |
:105150002481358146815781281739074A075B0723 |
:1051600028F0C8010E9495219E878D872D853E8558 |
:10517000232B09F4BCCF222009F0B9CFF8012481F8 |
:10518000358146815781B3CE61960FB6F894DEBF64 |
:105190000FBECDBFDF91CF911F910F91FF90EF9088 |
:1051A000DF90CF90BF90AF909F908F907F906F9047 |
:1051B0005F904F903F902F900895BF92CF92DF92D3 |
:1051C000EF92FF920F931F93CF93DF93CDB7DEB78C |
:1051D0002C970FB6F894DEBF0FBECDBF9C01B62E44 |
:1051E0007A0100E010E0CC24892B39F041155105FB |
:1051F00021F080918D06882319F480E090E041C071 |
:10520000D901FA01148A158A168A178A118F008F1C |
:105210008C91882331F58C2D992733C0D62E970198 |
:1052200048E16D2DCE0101960E943C22882399F41D |
:10523000F8018081882309F4DB2CA7016D2DCE01B4 |
:1052400001960E946B26882331F3F8018081882320 |
:1052500011F441E0C42ED8018C918823E1F2CC20D6 |
:10526000D1F6BE016F5F7F4FCD010E94931E8C016E |
:10527000009789F2FC016081662381F250E1D52E0E |
:10528000CECF2C960FB6F894DEBF0FBECDBFDF9108 |
:10529000CF911F910F91FF90EF90DF90CF90BF9033 |
:1052A0000895FF920F931F93CF93DF938C01F62EF7 |
:1052B000C0E0D0E080918D06882309F49BC00115E1 |
:1052C000110509F497C00E943F1EEC01892B09F4D7 |
:1052D00091C0188219821A821B821C821D821E8232 |
:1052E0001F8218861A861986FB861C861D861E86D6 |
:1052F0001F86188A198A1A8A1B8AC55EDD4F188292 |
:1053000019821A821B82CB51D2401C8A1D8A1E8AA6 |
:105310001F8A198E188E1A8E9E0148E16885C80171 |
:105320000E943E24882309F441C06F2D772767FD32 |
:1053300070956237710509F45AC0633771050CF432 |
:1053400045C067377105E1F58A8D99278C01017099 |
:10535000107080FD35C0688179818A819B810E94AF |
:10536000201F0E944B1FCE010E94D0200E94F21EDF |
:10537000DC01CB0188839983AA83BB838C839D83C3 |
:10538000AE83BF8318861A8709871C861D861E86F2 |
:105390001F86188A198A1A8A1B8A980140E050E0F1 |
:1053A00060E070E0CE010E94A52424C06F2D772715 |
:1053B00067FD70956136710591F06737710579F079 |
:1053C000CE010E949220C0E0D0E014C06136710589 |
:1053D000B9F78A8D80FDF4CF22E030E0DFCFAE0157 |
:1053E00060E2C8010E94DD28882321F4E9CF20E093 |
:1053F00030E0D4CFCE01DF91CF911F910F91FF907C |
:1054000008958F929F92AF92BF92CF92DF92EF92C8 |
:10541000FF920F931F93CF93C0E08BED97E09F9384 |
:105420008F93CF930E94300CC0938D060F900F90F6 |
:105430000F90E3ECF8E082E01082E05EFD4F8150D7 |
:1054400087FFFACF0E94961AC82F882309F0A9C0B7 |
:105450004FEB56E060E070E080E090E00E94B01D0D |
:10546000882309F0EAC080918108843009F4C2C021 |
:10547000863009F4BFC08E3009F4BCC0882499245A |
:1054800054018091CA069091CB068050924009F059 |
:10549000CBC08091CC0680938E064091CF0640937E |
:1054A0008F06C090D006D090D106D0929106C092BF |
:1054B00090062091D5063091D6063093930620931E |
:1054C00092068091CD069091CE067C01002711278F |
:1054D000E80CF91C0A1D1B1DE0929406F09295063B |
:1054E0000093960610939706C901AA27BB27242F7D |
:1054F000332744275527BC01CD010E94CD2EDC0166 |
:10550000CB018E0D9F1DA01FB11F80939806909315 |
:105510009906A0939A06B0939B06D294C2946FE02A |
:10552000C622CD24D622CD24960144275527280F04 |
:10553000391F4A1F5B1F20939C0630939D064093A2 |
:105540009E0650939F068091DF069091E006A09101 |
:10555000E106B091E2060097A105B10509F476C015 |
:10556000280F391F4A1F5B1F2150304040405040D8 |
:105570002093A0063093A1064093A2065093A30661 |
:105580008091F506863421F48091F6068134B9F0D5 |
:1055900088E798E09F938F931F920E94300CC6E09B |
:1055A00008C08CEE97E09F938F931F920E94300C5F |
:1055B000C1E00F900F900F900E94A72050C08091E3 |
:1055C000F706843529F78091F806813309F7809131 |
:1055D000F9068633E9F681E080938D06C0E08BE919 |
:1055E00098E09F938F93CF930E94300C0F900F9071 |
:1055F0000F9035C08090850890908608A09087080D |
:10560000B09088084FEB56E0C501B4010E94B01D70 |
:10561000882309F436CF85E298E09F938F931F92F9 |
:105620000E94300CC3E0C5CF8CE398E09F938F932A |
:105630001F920E94300CC4E0BCCF8EE098E09F9394 |
:105640008F93CF930E94300CC2E0B3CF8CE598E0EB |
:105650009F938F931F920E94300CC5E0AACF8C2F8E |
:105660009927CF911F910F91FF90EF90DF90CF90EE |
:10567000BF90AF909F908F900895AF92BF92CF92BE |
:10568000DF92EF92FF920F931F93CF93DF93EC0182 |
:105690007FEFA72EB72E80918D06882309F472C064 |
:1056A000209709F46FC02C853D854E855F852115B7 |
:1056B00031054105510509F465C0C888D988EA88D3 |
:1056C000FB88D701C6010196A11DB11D8217930762 |
:1056D000A407B50708F056C00C811D812E813F81BB |
:1056E0008885080F111D211D311DFE01E55EFD4F4E |
:1056F00080819181A281B38180179107A207B307AE |
:1057000089F00083118322833383CE014B96AC0151 |
:10571000C901B8010E94B01D882351F5C888D988F5 |
:10572000EA88FB8889859A85FE01E80FF91F238D99 |
:10573000A22EBB240894C11CD11CE11CF11CC88AF8 |
:10574000D98AEA8AFB8A019622E080309207C0F06B |
:105750001A86198688858F5F888790918E068917AB |
:1057600088F0CE010E949521892B29F018860AC065 |
:105770000E94A72007C088858150888780E092E03A |
:105780009A878987C501DF91CF911F910F91FF9073 |
:10579000EF90DF90CF90BF90AF9008958F929F923F |
:1057A000AF92BF92DF92EF92FF920F931F93CF932E |
:1057B000DF93D82E8B0180918D06882309F46FC06A |
:1057C0006115710509F46BC0FB012089318942899B |
:1057D000538984859585A685B785281739074A0793 |
:1057E0005B0708F050C0F80184809580A680B780E0 |
:1057F0008085880E911CA11CB11CEBE1EE2EE2E02D |
:10580000FE2EE00EF11EF70180819181A281B3810D |
:1058100088159905AA05BB0571F080829182A28244 |
:10582000B382C8014B96AC01C501B4010E94B01D02 |
:10583000882309F074C0F80161857285E60FF71FAF |
:10584000D38EF80184859585A685B7852089318911 |
:105850004289538982179307A407B50709F441C009 |
:105860002F5F3F4F4F4F5F4FF801208B318B428BA3 |
:10587000538BCB010196928781878050924090F4A0 |
:1058800080E090E050C0818592858050924008F47D |
:10589000AACFCB010E944725892B09F0A4CF8FEF17 |
:1058A0009FEF41C0C8014B96AC01F7016081718147 |
:1058B000828193810E94091DC82F882381F5F801F8 |
:1058C0001286118680858F5F808790918E0689175A |
:1058D000B8F2C8010E949521892B81F0F801C08798 |
:1058E000CFCF2F5F3F4F4F4F5F4F2487358746877E |
:1058F00057872150304040405040B2CFC8010E94ED |
:105900004725892B09F0BCCFF8018085815080871D |
:1059100080E092E0928781878FEF9FEF04C00E9422 |
:10592000A7208FEF9FEFDF91CF911F910F91FF90F5 |
:10593000EF90DF90BF90AF909F908F9008952F923F |
:105940003F925F926F927F928F929F92AF92BF929F |
:10595000CF92DF92EF92FF920F931F93CF93DF933B |
:10596000FC015A016B01380149011701EE24FF24A3 |
:105970008701A1E05A2E80918D068823A9F1211478 |
:10598000310491F1309781F1EF01E614F704080535 |
:10599000190538F5A114B104C104D104A1F05520B2 |
:1059A00091F0C1010E943D2B2FEF8F3F920709F12B |
:1059B00089930894A108B108C108D108A114B104C1 |
:1059C000C104D10461F7552029F00894E11CF11CB1 |
:1059D000011D111DE614F7040805190510F45520E2 |
:1059E000C9F6C801B70107C060E070E080E090E050 |
:1059F00002C05524CFCFDF91CF911F910F91FF901F |
:105A0000EF90DF90CF90BF90AF909F908F907F905E |
:105A10006F905F903F902F9008952F923F925F92EA |
:105A20006F927F928F929F92AF92BF92CF92DF92AE |
:105A3000EF92FF920F931F93CF93DF93FC015A01D4 |
:105A40006B01380149011701EE24FF248701B1E001 |
:105A50005B2E80918D068823A9F12114310491F1E8 |
:105A6000309781F1EF01E614F7040805190538F5C0 |
:105A7000A114B104C104D104A1F0552091F0B101E9 |
:105A800088810E94CE2B8F5F9F4F09F12196089449 |
:105A9000A108B108C108D108A114B104C104D104FE |
:105AA00061F7552029F00894E11CF11C011D111D1E |
:105AB000E614F7040805190510F45520C9F6C801C5 |
:105AC000B70107C060E070E080E090E002C05524BC |
:105AD000CFCFDF91CF911F910F91FF90EF90DF908B |
:105AE000CF90BF90AF909F908F907F906F905F907E |
:105AF0003F902F900895FF920F931F93CF93DF93C2 |
:105B0000EC018B01FF2420E030E080918D0688239A |
:105B1000F1F0672BE1F02097D1F088818823A9F07C |
:105B2000D901AC0FBD1FB8018C910E94CE2B9C01F6 |
:105B3000F394AF2DBB27FE01EA0FFB1F8081882362 |
:105B400021F08FEF2F3F380761F7C90102C080E0D5 |
:105B500090E0DF91CF911F910F91FF900895FC018C |
:105B600080918D06882339F0452B29F0309719F064 |
:105B7000623071051CF080E090E008951082CF0142 |
:105B80000895EF92FF920F931F93CF937C01C0E093 |
:105B90000E943F1E8C019C0148E16C2FC7010E94AE |
:105BA0003E24C82FC8010E94571E8C2F9927CF91E1 |
:105BB0001F910F91FF90EF900895FC0180899189CA |
:105BC000A289B3890196A11DB11D2485358546851D |
:105BD000578582179307A407B50718F480E090E073 |
:105BE000089581E090E00895CF93DF93FC01EB01ED |
:105BF00090E0309721F1672B11F18485882319F407 |
:105C0000892F99271EC080819181A281B3818883C9 |
:105C10009983AA83BB8384819581A681B7818C8374 |
:105C20009D83AE83BF8380859185A285B385888758 |
:105C30009987AA87BB8781E08C87982FE1CF80E086 |
:105C400090E0DF91CF910895FC01009781F01082E0 |
:105C50001182128213821482158216821782108694 |
:105C6000118612861386148681E090E008950895C7 |
:105C70008091180499278130910519F1823091059E |
:105C800084F0029719F01092180408958091A504E9 |
:105C90009091A6040E944D0F8823B9F31092180426 |
:105CA0000895892B81F78091C4039091C503816089 |
:105CB0009093C5038093C4038FE395E00E94242E44 |
:105CC000089588EE93E00E94440F9093A604809379 |
:105CD000A5048091C4039091C5038E7F9093C50362 |
:105CE0008093C4038091A7049091A8040196909397 |
:105CF000A8048093A7048091FB0399278370907078 |
:105D0000892B21F08091FA03843098F58FE395E098 |
:105D10000E94242E8091C6039091C703892BC1F065 |
:105D20008091F90380FF18C08091FA03843080F4D9 |
:105D30008091A7049091A80465E070E00E94EC2E89 |
:105D4000892B31F48AE090E09093E5038093E4039B |
:105D500082E08093180408958091A7049091A8048C |
:105D600065E070E00E94EC2E892BF1F684E690E06D |
:105D7000EBCF6FE375E08CEE93E00E94F42DCACF79 |
:105D8000FC014150504030F001900616D1F7319798 |
:105D9000CF010895882799270895629FD001739FA6 |
:105DA000F001829FE00DF11D649FE00DF11D929FB7 |
:105DB000F00D839FF00D749FF00D659FF00D9927F6 |
:105DC000729FB00DE11DF91F639FB00DE11DF91F1A |
:105DD000BD01CF0111240895AA1BBB1B51E107C0CF |
:105DE000AA1FBB1FA617B70710F0A61BB70B881F6B |
:105DF000991F5A95A9F780959095BC01CD010895FA |
:105E0000A1E21A2EAA1BBB1BFD010DC0AA1FBB1FBE |
:105E1000EE1FFF1FA217B307E407F50720F0A21B30 |
:105E2000B30BE40BF50B661F771F881F991F1A949D |
:105E300069F760957095809590959B01AC01BD01C7 |
:105E4000CF01089597FB092E05260ED057FD04D0EB |
:105E5000D7DF0AD0001C38F4509540953095219535 |
:105E60003F4F4F4F5F4F0895F6F79095809570958F |
:0A5E700061957F4F8F4F9F4F0895FB |
:105E7A007800416E616C6F675F4368302020202094 |
:105E8A002020416E616C6F675F43683120202020BB |
:105E9A002020416E616C6F675F43683220202020AA |
:105EAA002020416E616C6F675F4368332020202099 |
:105EBA002020416E616C6F675F4368342020202088 |
:105ECA002020416E616C6F675F4368352020202077 |
:105EDA002020416E616C6F675F4368362020202066 |
:105EEA002020416E616C6F675F4368372020202055 |
:105EFA0020205542617420202020202020202020AC |
:105F0A00202053706565645F4E6F7274682020208C |
:105F1A00202053706565645F4561737420202020DA |
:105F2A00202053706565645F546F70202020202004 |
:105F3A0020204E756D4F66536174732020202020F7 |
:105F4A002020506F732E4C6F6E67697475646520DC |
:105F5A002020506F732E4C6174697475646520201B |
:105F6A002020506F732E416C74697475646520200B |
:105F7A0020205A656C6C656E7A61686C202020203E |
:105F8A002020506F7765724F6E202020202020201D |
:105F9A002020446562756731382020202020202087 |
:105FAA002020446562756731392020202020202076 |
:105FBA00202044656275673230202020202020206E |
:105FCA00202044656275673231202020202020205D |
:105FDA00202044656275673232202020202020204C |
:105FEA00202044656275673233202020202020203B |
:105FFA00202044656275673234202020202020202A |
:10600A002020446562756732352020202020202018 |
:10601A002020446562756732362020202020202007 |
:10602A0020204465627567323720202020202020F6 |
:10603A0020204465627567323820202020202020E5 |
:10604A0020204465627567323920202020202020D4 |
:10605A0020204465627567333020202020202020CC |
:10606A0020204465627567333120202020202020BB |
:10607A002020F4016401FFFFFF48656C6C6F205714 |
:10608A006F726C6400000000000000000000000055 |
:10609A0000000000000000000000000000000000F6 |
:1060AA0000000000000000000000000000000000E6 |
:1060BA0000000000000000000000000000000000D6 |
:1060CA0000000000000000000003000000001F00A4 |
:1060DA0000003B0000005A00000078000000970012 |
:1060EA000000B5000000D4000000F3000000110118 |
:1060FA000000300100004E0100006D0100000000A8 |
:10610A0000001F0000003C0000005B000000790056 |
:10611A00000098000000B6000000D5000000F4005E |
:10612A00000012010000310100004F0100006E0161 |
:04613A000000010060 |
:00000001FF |
/FollowMe/analog.c |
---|
0,0 → 1,109 |
#include <stdlib.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "analog.h" |
volatile uint16_t Adc0, Adc1, Adc2, Adc3, Adc4, Adc5, Adc6, Adc7; |
volatile uint8_t ADReady = 1; |
/*****************************************************/ |
/* Initialize Analog Digital Converter */ |
/*****************************************************/ |
void ADC_Init(void) |
{ |
uint8_t sreg = SREG; |
// disable all interrupts before reconfiguration |
cli(); |
//ADC0 ... ADC7 is connected to PortA pin 0 ... 7 |
DDRA = 0x00; |
PORTA = 0x00; |
// Digital Input Disable Register 0 |
// Disable digital input buffer for analog adc_channel pins |
DIDR0 = 0xFF; |
// external reference AREF, adjust data to the right |
ADMUX &= ~((1 << REFS1)|(1 << REFS0)|(1 << ADLAR)); |
// set muxer to ADC adc_channel 0 (0 to 7 is a valid choice) |
ADMUX = (ADMUX & 0xE0) | 0x00; |
//Set ADC Control and Status Register A |
//Auto Trigger Enable, Prescaler Select Bits to Division Factor 128, i.e. ADC clock = SYSCKL/128 = 156.25 kHz |
ADCSRA = (0<<ADEN)|(0<<ADSC)|(0<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(0<<ADIE); |
//Set ADC Control and Status Register B |
//Trigger Source to Free Running Mode |
ADCSRB &= ~((1 << ADTS2)|(1 << ADTS1)|(1 << ADTS0)); |
// Start AD conversion |
ADC_Enable(); |
// restore global interrupt flags |
SREG = sreg; |
} |
/*****************************************************/ |
/* Interrupt Service Routine for ADC */ |
/*****************************************************/ |
// runs at 312.5 kHz or 3.2 µs |
// if after (60.8µs) all 19 states are processed the interrupt is disabled |
// and the update of further ads is stopped |
#define ADC0 0 |
#define ADC1 1 |
#define ADC2 2 |
#define ADC3 3 |
#define ADC4 4 |
#define ADC5 5 |
#define ADC6 6 |
#define ADC7 7 |
ISR(ADC_vect) |
{ |
static uint8_t ad_channel = ADC0, state = 0; |
// state machine |
switch(state++) |
{ |
case 0: |
Adc0 = ADC; |
ad_channel = ADC1; |
break; |
case 1: |
Adc1 = ADC; |
ad_channel = ADC2; |
break; |
case 2: |
Adc2 = ADC; |
ad_channel = ADC3; |
break; |
case 3: |
Adc3 = ADC; |
ad_channel = ADC4; |
break; |
case 4: |
Adc4 = ADC; |
ad_channel = ADC5; |
break; |
case 5: |
Adc5 = ADC; |
ad_channel = ADC6; |
break; |
case 6: |
Adc6 = ADC; |
ad_channel = ADC7; |
break; |
case 7: |
Adc7 = ADC; |
ad_channel = ADC0; |
state = 0; |
ADReady = 1; |
break; |
default: |
ad_channel = ADC0; |
state = 0; |
ADReady = 1; |
break; |
} |
// set adc muxer to next ad_channel |
ADMUX = (ADMUX & 0xE0) | ad_channel; |
// after full cycle stop further interrupts |
if(state != 0) ADC_Enable(); |
} |
/FollowMe/analog.h |
---|
0,0 → 1,20 |
#ifndef _ANALOG_H |
#define _ANALOG_H |
#include <inttypes.h> |
extern volatile uint16_t Adc0, Adc1, Adc2, Adc3, Adc4, Adc5, Adc6, Adc7; |
extern volatile uint8_t ADReady; |
void ADC_Init(void); |
// clear ADC enable & ADC Start Conversion & ADC Interrupt Enable bit |
#define ADC_Disable() (ADCSRA &= ~((1<<ADEN)|(1<<ADSC)|(1<<ADIE))) |
// set ADC enable & ADC Start Conversion & ADC Interrupt Enable bit |
#define ADC_Enable() (ADCSRA |= (1<<ADEN)|(1<<ADSC)|(1<<ADIE)) |
#endif //_ANALOG_H |
/FollowMe/button.c |
---|
0,0 → 1,50 |
#include "timer0.h" |
#include "button.h" |
#ifdef USE_FOLLOWME |
#define BUTTON !(PINC & (1<<PINC6)) |
#endif |
#ifdef USE_SDLOGGER |
#define BUTTON !(PINC & (1<<PINC3)) |
#endif |
#define CNT_KEY 10 // at least 3 |
#define KEY_DELAY_MS 50 |
uint16_t ButtonTimer = 0; |
void Button_Init(void) |
{ |
// set port pin as input pullup |
#ifdef USE_FOLLOWME |
PORTC |= (1 << PORTC6); |
DDRC &= ~(1 << DDC6); |
#endif |
#ifdef USE_SDLOGGER |
PORTC |= (1 << PORTC3); |
DDRC &= ~(1 << DDC3); |
#endif |
ButtonTimer = SetDelay(KEY_DELAY_MS); |
} |
uint8_t GetButton(void) |
{ |
static uint8_t button = 0; |
uint8_t ret = 0; |
if(CheckDelay(ButtonTimer)) |
{ |
if(BUTTON) |
{ |
if(button++ == 0 || button == CNT_KEY) ret = 1; |
if(button == CNT_KEY) button = CNT_KEY - CNT_KEY / 3; |
} |
else button = 0; |
ButtonTimer = SetDelay(KEY_DELAY_MS); |
} |
return(ret); |
} |
/FollowMe/button.h |
---|
0,0 → 1,11 |
#ifndef _BUTTON_H |
#define _BUTTON_H |
#include <avr/io.h> |
#include <inttypes.h> |
extern void Button_Init(void); |
extern uint8_t GetButton(void); |
#endif //_BUTTON_H |
/FollowMe/crc16.c |
---|
0,0 → 1,48 |
#include "crc16.h" |
#include <avr/pgmspace.h> |
const uint16_t crc16tab[256] PROGMEM = |
{ |
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, |
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, |
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, |
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, |
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, |
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, |
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, |
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, |
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, |
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, |
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, |
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, |
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, |
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, |
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, |
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, |
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, |
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, |
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, |
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, |
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, |
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, |
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, |
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, |
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, |
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, |
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, |
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, |
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, |
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, |
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, |
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 |
}; |
uint16_t CRC16(const uint8_t *pBuffer, uint32_t len) |
{ |
uint32_t counter; |
uint16_t crc = 0; |
for( counter = 0; counter < len; counter++) |
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *pBuffer++)&0x00FF]; |
return crc; |
} |
/FollowMe/crc16.h |
---|
0,0 → 1,8 |
#ifndef _CRC16_H |
#define _CRC16_H |
#include <inttypes.h> |
extern uint16_t CRC16(const uint8_t * pBuffer, uint32_t len); |
#endif // _CRC16_H |
/FollowMe/fat16.c |
---|
0,0 → 1,1741 |
#include <string.h> |
#include "printf_P.h" |
#include "timer0.h" |
#include "fat16.h" |
#include "sdc.h" |
#include "uart1.h" |
/* |
FAT16 Drive Layout: |
Description Offset |
Volume Boot Sector Start of Partition |
Fat Tables Start + # of Reserved Sectors |
Root Directory Entry Start + # of Reserved + (# of Sectors Per FAT * 2) |
Data Area (Starts with Cluster #2) Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
*/ |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an partition entry |
________________________________________________________________________________________________________________________________________ |
Partition Entry is 16 bytes long |
*/ |
typedef struct |
{ |
uint8_t PartitionState; // Current State of Partition (00h=Inactive, 80h=Active) |
uint8_t BeginningHead; // Beginning of Partition - Head |
uint16_t BeginningCylSec; // Beginning of Partition - Cylinder/Sector (See Below) |
uint8_t Type; // Type of Partition (See List Below) |
uint8_t EndHead; // End of Partition - Head |
uint16_t EndCylSec; // End of Partition - Cylinder/Sector |
uint32_t NoSectorsBeforePartition; // Number of Sectors between the MBR and the First Sector in the Partition |
uint32_t NoSectorsPartition ; // Number of Sectors in the Partition |
} __attribute__((packed)) PartitionEntry_t; |
/* |
Coding of Cylinder/Sector words |
Cylinder is 10 bits: [7:0] at [15:8] and [9:8] at [7:6] |
Sector is 5 bits: [5:0] at [5:0] |
*/ |
// Partition Types: |
#define PART_TYPE_UNKNOWN 0x00 |
#define PART_TYPE_FAT12 0x01 |
#define PART_TYPE_XENIX 0x02 |
#define PART_TYPE_FAT16_ST_32_MB 0x04 |
#define PART_TYPE_EXTDOS 0x05 |
#define PART_TYPE_FAT16_LT_32_MB 0x06 |
#define PART_TYPE_NTFS 0x07 |
#define PART_TYPE_FAT32 0x0B |
#define PART_TYPE_FAT32LBA 0x0C |
#define PART_TYPE_FAT16LBA 0x0E |
#define PART_TYPE_EXTDOSLBA 0x0F |
#define PART_TYPE_EISA 0x12 |
#define PART_TYPE_ONTRACK 0x33 |
#define PART_TYPE_NOVELL 0x40 |
#define PART_TYPE_DYNAMIC 0x42 |
#define PART_TYPE_PCIX 0x4B |
#define PART_TYPE_LINUX_SWAP 0x82 |
#define PART_TYPE_LINUX_NATIVE 0x83 |
#define PART_TYPE_LINUX_LVM 0x8E |
#define PART_TYPE_PHOENIXSAVE 0xA0 |
#define PART_TYPE_FREEBSD 0xA5 |
#define PART_TYPE_OPENBSD 0xA6 |
#define PART_TYPE_NETNBSD 0xA9 |
#define PART_TYPE_CPM 0xDB |
#define PART_TYPE_DBFS 0xE0 |
#define PART_TYPE_BBT 0xFF |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of the MasterBootRecord |
________________________________________________________________________________________________________________________________________ |
Master Boot Record is 512 bytes long |
The Master Boot Record is the same for pretty much all Operating Systems. |
It is located on the first Sector of the Hard Drive, at Cylinder 0, Head 0, Sector 1 |
*/ |
typedef struct |
{ |
uint8_t ExecutableCode[446]; // 446 bytes for machine start code |
PartitionEntry_t PartitionEntry1; // 16 bytes for partition entry 1 |
PartitionEntry_t PartitionEntry2; // 16 bytes for partition entry 2 |
PartitionEntry_t PartitionEntry3; // 16 bytes for partition entry 3 |
PartitionEntry_t PartitionEntry4; // 16 bytes for partition entry 4 |
uint16_t ExecutableMarker; // BIOS-Signature (0x55 0xAA) |
} __attribute__((packed)) MBR_Entry_t; |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of the VolumeBootRecord |
________________________________________________________________________________________________________________________________________ |
The Volume Boot Record is 512 bytes long |
This information is located in the first sector of every partition. |
*/ |
typedef struct |
{ |
uint8_t JumpCode[3]; // Jump Code + NOP |
int8_t OEMName[8]; // OEM Name |
uint16_t BytesPerSector; // Bytes Per Sector |
uint8_t SectorsPerCluster; // Sectors Per Cluster |
uint16_t ReservedSectors; // Reserved Sectors |
uint8_t NoFATCopies; // Number of Copies of FAT |
uint16_t MaxRootEntries; // Maximum Root Directory Entries |
uint16_t NoSectorsInPartSml32MB; // Number of Sectors in Partition Smaller than 32 MB |
uint8_t MediaDescriptor; // Media Descriptor (0xF8 for Hard Disks) |
uint16_t SectorsPerFAT; // Sectors Per FAT |
uint16_t SectorsPerTrack; // Sectors Per Track |
uint16_t NoHeads; // Number of Heads |
uint32_t NoHiddenSectors; // Number of Hidden Sectors in Partition |
uint32_t NoSectors; // Number of Sectors in Partition |
uint16_t DriveNo; // Logical Drive Number of Partition |
uint8_t ExtendedSig; // Extended Signature (0x29) |
uint32_t SerialNo; // Serial Number of the Partition |
int8_t VolumeName[11]; // Volume Name of the Partititon |
int8_t FATName[8]; // FAT Name (FAT16) |
uint8_t ExecutableCode[446]; // 446 bytes for machine start code |
uint16_t ExecutableMarker; // Executable Marker (0x55 0xAA) |
} __attribute__((packed)) VBR_Entry_t; |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an directory entry |
________________________________________________________________________________________________________________________________________ |
Directory entry is 32 bytes. |
*/ |
typedef struct |
{ |
int8_t Name[8]; // 8 bytes name, padded with spaces. |
uint8_t Extension[3]; // 3 bytes extension, padded with spaces. |
uint8_t Attribute; // attribute of the directory entry (unused,archive,read-only,system,directory,volume) |
uint8_t Reserved[10]; // reserved bytes within the directory entry. |
uint32_t DateTime; // date and time of last write access to the file or directory. |
uint16_t StartCluster; // first cluster of the file or directory. |
uint32_t Size; // size of the file or directory in bytes. |
} __attribute__((packed)) DirEntry_t; |
#define SLOT_EMPTY 0x00 // slot has never been used |
#define SLOT_E5 0x05 // the real value is 0xe5 |
#define SLOT_DELETED 0xE5 // file in this slot deleted |
#define ATTR_NONE 0x00 // normal file |
#define ATTR_READONLY 0x01 // file is readonly |
#define ATTR_HIDDEN 0x02 // file is hidden |
#define ATTR_SYSTEM 0x04 // file is a system file |
#define ATTR_VOLUMELABEL 0x08 // entry is a volume label |
#define ATTR_LONG_FILENAME 0x0F // this is a long filename entry |
#define ATTR_SUBDIRECTORY 0x10 // entry is a directory name |
#define ATTR_ARCHIVE 0x20 // file is new or modified |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an entry within the fileallocationtable. |
________________________________________________________________________________________________________________________________________ |
*/ |
typedef struct |
{ |
uint16_t NextCluster; // the next cluster of the file. |
} __attribute__((packed)) Fat16Entry_t; |
// secial fat entries |
#define FAT16_CLUSTER_FREE 0x0000 |
#define FAT16_CLUSTER_RESERVED 0x0001 |
#define FAT16_CLUSTER_USED_MIN 0x0002 |
#define FAT16_CLUSTER_USED_MAX 0xFFEF |
#define FAT16_CLUSTER_ROOTDIR_MIN 0xFFF0 |
#define FAT16_CLUSTER_ROOTDIR_MAX 0xFFF6 |
#define FAT16_CLUSTER_BAD 0xFFF7 |
#define FAT16_CLUSTER_LAST_MIN 0xFFF8 |
#define FAT16_CLUSTER_LAST_MAX 0xFFFF |
/*****************************************************************************************************************************************/ |
/* */ |
/* Global variables needed for read- or write-acces to the FAT16- filesystem. */ |
/* */ |
/*****************************************************************************************************************************************/ |
#define MBR_SECTOR 0x00 // the masterboot record is located in sector 0. |
#define DIRENTRY_SIZE 32 //bytes |
#define DIRENTRIES_PER_SECTOR BYTES_PER_SECTOR/DIRENTRY_SIZE |
#define FAT16_BYTES 2 |
#define FAT16_ENTRIES_PER_SECTOR BYTES_PER_SECTOR/FAT16_BYTES |
#define FSTATE_UNUSED 0 |
#define FSTATE_USED 1 |
typedef struct |
{ |
uint8_t IsValid; // 0 means invalid, else valid |
uint8_t SectorsPerCluster; // how many sectors does a cluster contain? |
uint8_t FatCopies; // Numbers of copies of the FAT |
uint16_t MaxRootEntries; // Possible number of entries in the root directory. |
uint16_t SectorsPerFat; // how many sectors does a fat16 contain? |
uint32_t FirstFatSector; // sector of the start of the fat |
uint32_t FirstRootDirSector; // sector of the rootdirectory |
uint32_t FirstDataSector; // sector of the first cluster containing data (cluster2). |
uint32_t LastDataSector; // the last data sector of the partition |
} Partition_t; |
Partition_t Partition; // Structure holds partition information |
File_t FilePointer[FILE_MAX_OPEN]; // Allocate Memmoryspace for each filepointer used. |
/****************************************************************************************************************************************/ |
/* Function: FileDateTime(DateTime_t *); */ |
/* */ |
/* Description: This function calculates the DOS date time from a pointer to a time structure. */ |
/* */ |
/* Returnvalue: Returns the DOS date time. */ |
/****************************************************************************************************************************************/ |
uint32_t FileDateTime(DateTime_t * pTimeStruct) |
{ |
uint32_t datetime = 0; |
if((pTimeStruct == 0) || !(pTimeStruct->Valid)) return datetime; |
datetime |= (0x0000007FL & (uint32_t)(pTimeStruct->Year - 1980))<<25; // set year |
datetime |= (0x0000000FL & (uint32_t)(pTimeStruct->Month))<<21; // set month |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Day))<<16; |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Hour))<<11; |
datetime |= (0x0000003FL & (uint32_t)(pTimeStruct->Min))<<5; |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Sec/2)); |
return datetime; |
} |
/****************************************************************************************************************************************/ |
/* Function: LockFilePointer(); */ |
/* */ |
/* Description: This function trys to lock a free file pointer. */ |
/* */ |
/* Returnvalue: Returns the Filepointer on success or 0. */ |
/****************************************************************************************************************************************/ |
File_t * LockFilePointer(void) |
{ |
uint8_t i; |
File_t * File = 0; |
for(i = 0; i < FILE_MAX_OPEN; i++) |
{ |
if(FilePointer[i].State == FSTATE_UNUSED) // found an unused one |
{ |
File = &FilePointer[i]; // set pointer to that entry |
FilePointer[i].State = FSTATE_USED; // mark it as used |
break; |
} |
} |
return(File); |
} |
/****************************************************************************************************************************************/ |
/* Function: UnlockFilePointer(file_t *); */ |
/* */ |
/* Description: This function trys to unlock a file pointer. */ |
/* */ |
/* Returnvalue: Returns 1 if file pointer was freed else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t UnlockFilePointer(File_t * file) |
{ |
uint8_t cnt; |
if(file == NULL) return(0); |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
if(&FilePointer[cnt] == file) // filepointer to be freed found? |
{ |
file->State = FSTATE_UNUSED; |
file->FirstSectorOfFirstCluster = 0; // Sectorpointer to the first sector of the first datacluster of the file. |
file->FirstSectorOfCurrCluster = 0; |
file->SectorOfCurrCluster = 0; // Pointer to the cluster which is edited at the moment. |
file->SectorOfCurrCluster = 0; // The sector which is edited at the moment (cluster_pointer + sector_index). |
file->ByteOfCurrSector = 0; // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index). |
file->Mode = 0; // mode of fileoperation (read,write) |
file->Size = 0; // the size of the opend file in bytes. |
file->Position = 0; // pointer to a character within the file 0 < fileposition < filesize |
file->SectorInCache = 0; // the last sector read, wich is still in the sectorbuffer. |
file->DirectorySector = 0; // the sectorposition where the directoryentry has been made. |
file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
file->Attribute = 0; // the attribute of the file opened. |
file = NULL; |
return(1); |
} |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: SeperateDirName(int8_t*, int8_t*); */ |
/* */ |
/* Description: This function seperates the first dirname from filepath and brings them */ |
/* into the needed format ('test.txt' -> 'TEST TXT') */ |
/* The subpath is the pointer to the remaining substring if the filepath */ |
/* */ |
/* Returnvalue: Return NULL on error or pointer to subpath */ |
/****************************************************************************************************************************************/ |
int8_t* SeperateDirName(const int8_t *filepath, int8_t *dirname) |
{ |
int8_t* subpath = NULL; |
uint8_t readpointer = 0; |
uint8_t writepointer = 0; |
// search subpath from beginning of filepath |
subpath = NULL; |
readpointer = 0; |
if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
while(subpath == NULL) // search the filepath until a subpath was found. |
{ |
if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/'))) // if '/' found or end of filepath reached |
{ |
subpath = (int8_t*)&filepath[readpointer]; // store the position of the first "/" found after the beginning of the filenpath |
} |
readpointer++; |
} |
// clear dirname with spaces |
dirname[11] = 0; // terminate dirname |
for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' '; |
writepointer = 0; |
// start seperating the dirname from the filepath. |
readpointer = 0; |
if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
while( &filepath[readpointer] < subpath) |
{ |
if(writepointer >= 11) return(NULL); // dirname to long |
if(filepath[readpointer] == '.') // seperating dirname and extension. |
{ |
if(writepointer <= 8) |
{ |
readpointer++; // next character in filename |
writepointer = 8; // jump to start of extension |
} |
else return(NULL); // dirbasename to long |
} |
else |
{ |
if((0x60 < filepath[readpointer]) && (filepath[readpointer] < 0x7B)) |
{ |
dirname[writepointer] = (filepath[readpointer] - 0x20); // all characters must be upper case. |
} |
else |
{ |
dirname[writepointer] = filepath[readpointer]; |
} |
readpointer++; |
writepointer++; |
} |
} |
return(subpath); |
} |
/**************************************************************************************************************************************+*/ |
/* Function: Fat16ClusterToSector( uint16_t cluster); */ |
/* */ |
/* Description: This function converts a cluster number given by the fat to the corresponding */ |
/* sector that points to the start of the data area that is represented by the cluster number. */ |
/* */ |
/* Returnvalue: The sector number with the data area of the given cluster */ |
/****************************************************************************************************************************************/ |
uint32_t Fat16ClusterToSector(uint16_t cluster) |
{ |
if(!Partition.IsValid) return 0; |
if (cluster < 2) cluster = 2; // the 0. and 1. cluster in the fat are used for the media descriptor |
return ( (cluster - 2) * Partition.SectorsPerCluster) + Partition.FirstDataSector; // the first data sector is represented by the 2nd cluster |
} |
/****************************************************************************************************************************************/ |
/* Function: SectorToFat16Cluster( uint32_t sector); */ |
/* */ |
/* Description: This function converts a given sector number given to the corresponding */ |
/* cluster number in the fat that represents this data area. */ |
/* */ |
/* Returnvalue: The cluster number representing the data area of the sector. */ |
/****************************************************************************************************************************************/ |
uint16_t SectorToFat16Cluster(uint32_t sector) |
{ |
if(!Partition.IsValid) return 0; |
return ((uint16_t)((sector - Partition.FirstDataSector) / Partition.SectorsPerCluster) + 2); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_Deinit(void); */ |
/* */ |
/* Description: This function uninitializes the fat 16 api */ |
/* */ |
/* Returnvalue: The function returns "0" on success */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_Deinit(void) |
{ |
int16_t returnvalue = 0; |
uint8_t cnt; |
// declare the filepointers as unused. |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
if(FilePointer[cnt].State == FSTATE_USED) |
{ |
returnvalue += fclose_(&FilePointer[cnt]); // try to close open file pointers |
} |
} |
SDC_Deinit(); // uninitialize interface to sd-card |
Partition.IsValid = 0; // mark data in partition structure as invalid |
return(returnvalue); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_Init(void); */ |
/* */ |
/* Description: This function reads the Masterbootrecord and finds the position of the Volumebootrecord, the FAT and the Rootdir */ |
/* and stores the information in global variables. */ |
/* */ |
/* Returnvalue: The function returns "0" if the filesystem is initialized. */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_Init(void) |
{ |
uint8_t cnt = 0; |
uint32_t partitionfirstsector; |
VBR_Entry_t *VBR; |
MBR_Entry_t *MBR; |
File_t *file; |
uint8_t result = 0; |
printf("\r\n FAT16 init..."); |
Partition.IsValid = 0; |
// declare the filepointers as unused. |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
FilePointer[cnt].State = FSTATE_UNUSED; |
} |
// set current file pinter to first position in list |
file = &FilePointer[0]; |
// try to initialise the sd-card. |
if(SD_SUCCESS != SDC_Init()) |
{ |
printf("SD-Card could not be initialized."); |
result = 1; |
goto end; |
} |
// SD-Card is initialized successfully |
if(SD_SUCCESS != SDC_GetSector((uint32_t)MBR_SECTOR,file->Cache)) // Read the MasterBootRecord |
{ |
printf("Error reading the MBR."); |
result = 2; |
goto end; |
} |
MBR = (MBR_Entry_t *)file->Cache; // Enter the MBR using the structure MBR_Entry_t. |
if((MBR->PartitionEntry1.Type == PART_TYPE_FAT16_ST_32_MB) || |
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16_LT_32_MB) || |
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16LBA)) |
{ |
// get sector offset 1st partition |
partitionfirstsector = MBR->PartitionEntry1.NoSectorsBeforePartition; |
// Start of Partition is the Volume Boot Sector |
if(SD_SUCCESS != SDC_GetSector(partitionfirstsector,file->Cache)) // Read the volume boot record |
{ |
printf("Error reading the VBR."); |
result = 3; |
goto end; |
} |
} |
else // maybe the medium has no partition assuming sector 0 is the vbr |
{ |
partitionfirstsector = 0; |
} |
VBR = (VBR_Entry_t *) file->Cache; // Enter the VBR using the structure VBR_Entry_t. |
if(VBR->BytesPerSector != BYTES_PER_SECTOR) |
{ |
printf("VBR: Sector size not supported."); |
result = 4; |
goto end; |
} |
Partition.SectorsPerCluster = VBR->SectorsPerCluster; // Number of sectors per cluster. Depends on the memorysize of the sd-card. |
Partition.FatCopies = VBR->NoFATCopies; // Number of fatcopies. |
Partition.MaxRootEntries = VBR->MaxRootEntries; // How many Entries are possible in the rootdirectory (FAT16 allows max. 512 entries). |
Partition.SectorsPerFat = VBR->SectorsPerFAT; // The number of sectors per FAT. |
/* Calculate the sectorpositon of the FAT, the Rootdirectory and the first Datacluster. */ |
// Calculate the position of the FileAllocationTable: |
// Start + # of Reserved Sectors |
Partition.FirstFatSector = (uint32_t)(partitionfirstsector + (uint32_t)(VBR->ReservedSectors)); |
// Calculate the position of the Rootdirectory: |
// Start + # of Reserved Sectors + (# of Sectors Per FAT * # of FAT Copies) |
Partition.FirstRootDirSector = Partition.FirstFatSector + (uint32_t)((uint32_t)Partition.SectorsPerFat*(uint32_t)Partition.FatCopies); |
// Calculate the position of the first datacluster: |
// Start + # of Reserved + (# of Sectors Per FAT * # of FAT Copies) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
Partition.FirstDataSector = Partition.FirstRootDirSector + (uint32_t)(Partition.MaxRootEntries>>4); // assuming 512 Byte Per Sector |
// Calculate the last data sector |
if(VBR->NoSectors == 0) |
{ |
printf("VBR: Bad number of sectors."); |
result = 5; |
goto end; |
} |
Partition.LastDataSector = Partition.FirstDataSector + VBR->NoSectors - 1; |
// check for FAT16 in VBR of first partition |
if(!((VBR->FATName[0]=='F') && (VBR->FATName[1]=='A') && (VBR->FATName[2]=='T') && (VBR->FATName[3]=='1')&&(VBR->FATName[4]=='6'))) |
{ |
printf("VBR: Partition ist not FAT16 type."); |
result = 6; |
goto end; |
} |
Partition.IsValid = 1; // mark data in partition structure as valid |
result = 0; |
end: |
if(result != 0) Fat16_Deinit(); |
else printf(" ...ok"); |
return(result); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_IsValid(void); */ |
/* */ |
/* Description: This function return the Fat 15 filesystem state */ |
/* */ |
/* Returnvalue: The function returns "1" on success */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_IsValid(void) |
{ |
return(Partition.IsValid); |
} |
/****************************************************************************************************************************************/ |
/* Function: ClearCurrCluster(File_t*); */ |
/* */ |
/* Description: This function fills the current cluster with 0. */ |
/* */ |
/* Returnvalue: The function returns 1 on success else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t ClearCurrCluster(File_t * file) |
{ |
uint8_t retvalue = 1; |
uint32_t i; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
for(i = 0; i < BYTES_PER_SECTOR; i++) file->Cache[i] = 0; // clear file cache |
for(i = 0; i < Partition.SectorsPerCluster; i++) |
{ |
file->SectorInCache = file->FirstSectorOfCurrCluster + i; |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
retvalue = 0; |
} |
} |
return(retvalue); |
} |
/*****************************************************************************************************************************************/ |
/* Function: GetNextCluster(File_t* ); */ |
/* */ |
/* Description: This function finds the next datacluster of the file specified with File *File. */ |
/* */ |
/* Returnvalue: The function returns the next cluster or 0 if the last cluster has already reached. */ |
/*****************************************************************************************************************************************/ |
uint16_t GetNextCluster(File_t * file) |
{ |
uint16_t cluster = 0; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(cluster); |
// if sector is within the data area |
if((Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)&& (file->FirstSectorOfCurrCluster <= Partition.LastDataSector)) |
{ |
// determine current file cluster |
cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster); |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
// read this sector to the file cache |
if(file->SectorInCache != sector) |
{ |
file->SectorInCache = sector; // update sector stored in buffer |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
{ |
Fat16_Deinit(); |
return (cluster); |
} |
} |
// read the next cluster from cache |
fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
cluster = fat->NextCluster; |
// if last cluster fat entry |
if(FAT16_CLUSTER_LAST_MIN <= cluster) |
{ |
cluster = 0; |
} |
else |
{ |
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(cluster); |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
} |
} |
return(cluster); |
} |
/****************************************************************************************************************************************/ |
/* Function: FindNextFreeCluster(File_t *); */ |
/* */ |
/* Description: This function looks in the fat to find the next free cluster */ |
/* */ |
/* Returnvalue: The function returns the cluster number of the next free cluster found within the fat. */ |
/****************************************************************************************************************************************/ |
uint16_t FindNextFreeCluster(File_t *file) |
{ |
uint32_t fat_sector; // current sector within the fat relative to the first sector of the fat. |
uint32_t curr_sector; // current sector |
uint16_t fat_entry; // index to an fatentry within the actual sector (256 fatentries are possible within one sector). |
uint16_t free_cluster = 0; // next free cluster number. |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
// start searching for an empty cluster at the beginning of the fat. |
fat_sector = 0; |
do |
{ |
curr_sector = Partition.FirstFatSector + fat_sector; // calculate sector to read |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector of fat from sd-card. |
{ |
Fat16_Deinit(); |
return(free_cluster); |
} |
fat = (Fat16Entry_t *)file->Cache; // set fat pointer to file cache |
for(fat_entry = 0; fat_entry < FAT16_ENTRIES_PER_SECTOR; fat_entry++) // look for an free cluster at all entries in this sector of the fat. |
{ |
if(fat[fat_entry].NextCluster == FAT16_CLUSTER_FREE) // empty cluster found!! |
{ |
fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX; // mark this fat-entry as used |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // and save the sector at the sd-card. |
{ |
Fat16_Deinit(); |
return(free_cluster); |
} |
free_cluster = (uint16_t)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (uint32_t)fat_entry); |
fat_entry = FAT16_ENTRIES_PER_SECTOR; // terminate the search for a free cluster in this sector. |
} |
} |
fat_sector++; // continue the search in next fat sector |
// repeat until the end of the fat is reached and no free cluster has been found so far |
}while((fat_sector < Partition.SectorsPerFat) && (!free_cluster)); |
return(free_cluster); |
} |
/****************************************************************************************************************************************************/ |
/* Function: int16_t fseek_(File_t *, int32_t *, uint8_t) */ |
/* */ |
/* Description: This function sets the pointer of the stream relative to the position */ |
/* specified by origin (SEEK_SET, SEEK_CUR, SEEK_END) */ |
/* Returnvalue: Is 1 if seek was successful */ |
/****************************************************************************************************************************************************/ |
int16_t fseek_(File_t * const file, int32_t offset, int16_t origin) |
{ |
int32_t fposition = 0; |
int16_t retvalue = 1; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
switch(origin) |
{ |
case SEEK_SET: // Fileposition relative to the beginning of the file. |
fposition = 0; |
break; |
case SEEK_END: // Fileposition relative to the end of the file. |
fposition = (int32_t)file->Size; |
break; |
case SEEK_CUR: // Fileposition relative to the current position of the file. |
default: |
fposition = file->Position; |
break; |
} |
fposition += offset; |
if((fposition >= 0) && (fposition <= (int32_t)file->Size)) // is the pointer still within the file? |
{ |
// reset file position to start of the file |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->Position = 0; |
while(file->Position < fposition) // repeat until the current position is less than target |
{ |
file->Position++; // increment file position |
file->ByteOfCurrSector++; // next byte in current sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) |
{ |
file->ByteOfCurrSector = 0; // reading at the beginning of new sector. |
file->SectorOfCurrCluster++; // continue reading in next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster) // if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ |
file->SectorOfCurrCluster = 0; |
} |
else // the last cluster was allready reached |
{ |
file->SectorOfCurrCluster--; // jump back to the ast sector in the last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set ByteOfCurrSector one byte over sector end |
} |
} |
} |
} |
} |
if(file->Position == fposition) retvalue = 0; |
return(retvalue); |
} |
/****************************************************************************************************************************************/ |
/* Function: uint16_t DeleteClusterChain(File *file); */ |
/* */ |
/* Description: This function trances along a cluster chain in the fat and frees all clusters visited. */ |
/* */ |
/****************************************************************************************************************************************/ |
uint8_t DeleteClusterChain(uint16_t StartCluster) |
{ |
uint16_t cluster; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
uint8_t buffer[BYTES_PER_SECTOR]; |
uint32_t sector_in_buffer = 0; |
uint8_t repeat = 0; |
if(!Partition.IsValid) return 0; |
cluster = StartCluster; // init chain trace |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
do |
{ |
if(sector != sector_in_buffer) |
{ |
// read this sector to buffer |
sector_in_buffer = sector; |
if(SD_SUCCESS != SDC_GetSector(sector_in_buffer, buffer)) return 0; // read sector from sd-card |
} |
// read the next cluster from cache |
fat = (Fat16Entry_t *)(&(buffer[byte])); |
cluster = fat->NextCluster; |
if((FAT16_CLUSTER_USED_MIN <= cluster) && (cluster <= FAT16_CLUSTER_USED_MAX) ) repeat = 1; |
else repeat = 0; |
fat->NextCluster = FAT16_CLUSTER_FREE; // mark current cluster as free |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
// if new sector is not the sector in buffer or the last cluster in the chain was traced |
if((sector != sector_in_buffer) || !repeat) |
{ // write sector in buffer |
if(SD_SUCCESS != SDC_PutSector(sector_in_buffer,buffer)) return 0; |
} |
} |
while(repeat); |
return 1; |
} |
/****************************************************************************************************************************************/ |
/* Function: uint16_t AppendCluster(File *file); */ |
/* */ |
/* Description: This function looks in the fat to find the next free cluster and appends it to the file. */ |
/* */ |
/* Returnvalue: The function returns the appened cluster number or 0 of no cluster was appended. */ |
/****************************************************************************************************************************************/ |
uint16_t AppendCluster(File_t *file) |
{ |
uint16_t last_cluster, new_cluster = 0; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(new_cluster); |
new_cluster = FindNextFreeCluster(file); // the next free cluster found on the disk. |
if(new_cluster) |
{ // A free cluster was found and can be added to the end of the file. |
fseek_(file, 0, SEEK_END); // jump to the end of the file |
last_cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster); // determine current file cluster |
fat_byte_offset = ((uint32_t)last_cluster)<<1; |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
if(file->SectorInCache != sector) |
{ |
file->SectorInCache = sector; // update sector stored in buffer |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
{ |
Fat16_Deinit(); |
return(0); |
} |
} |
fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
fat->NextCluster = new_cluster; // append the free cluster to the end of the file in the FAT. |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // save the modified sector to the FAT. |
{ |
Fat16_Deinit(); |
return(0); |
} |
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(new_cluster); |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
} |
return(new_cluster); |
} |
/****************************************************************************************************************************************************/ |
/* Function: DirectoryEntryExist(int8_t *, uint8_t, uint8_t, File_t *) */ |
/* */ |
/* Description: This function searches all possible dir entries until the file or directory is found or the end of the directory is reached */ |
/* */ |
/* Returnvalue: This function returns 1 if the directory entry specified was found. */ |
/****************************************************************************************************************************************************/ |
uint8_t DirectoryEntryExist(int8_t *dirname, uint8_t attribfilter, uint8_t attribmask, File_t *file) |
{ |
uint32_t dir_sector, max_dir_sector, curr_sector; |
uint16_t dir_entry = 0; |
uint16_t end_of_directory_not_reached = 0; |
uint8_t i = 0; |
uint8_t direntry_exist = 0; |
DirEntry_t * dir; |
// if incomming pointers are useless return immediatly |
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist); |
// dir entries can be searched only in filesclusters that have |
// a corresponding dir entry with adir-flag set in its attribute |
// or direct within the root directory area |
file->FirstSectorOfFirstCluster = 0; |
// no current directory exist therefore assume searching in the root |
if(file->DirectorySector == 0) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector; |
} |
// within the root directory area we can read sectors sequentially until the end of this area |
else if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.FirstDataSector)) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
} |
// within the data clusters we can read sectors sequentially only within the cluster |
else if((Partition.FirstDataSector <= file->DirectorySector) && (file->DirectorySector <= Partition.LastDataSector)) |
{ |
max_dir_sector = Partition.SectorsPerCluster; // limit max secters before next cluster |
} |
else return (direntry_exist); // bad sector range for directory sector of the file |
// if search area is not defined yet |
if(file->FirstSectorOfFirstCluster == 0) |
{ |
// check if the directory entry of current file is existent and has the dir-flag set |
file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(direntry_exist); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
switch((uint8_t)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
// the directrory pointer of this file points to a deleted or not existen directory |
// therefore no file or subdirectory can be created |
return (direntry_exist); |
break; |
default: // and is a real directory |
if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY) |
{ // current file is not a directory therefore no file or subdirectory can be created here |
return (direntry_exist); |
} |
break; |
} |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[file->DirectoryIndex].StartCluster); |
} |
// update current file data area position to start of first cluster |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
do // loop over all data clusters of the current directory entry |
{ |
dir_sector = 0; // reset sector counter within a new cluster |
do // loop over all sectors of a cluster or all sectors of the root directory |
{ |
curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
{ |
Fat16_Deinit(); |
return(direntry_exist); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
// search all directory entries within that sector |
for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
{ // check for existing dir entry |
switch((uint8_t)dir[dir_entry].Name[0]) |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
// ignore empty or deleted dir entries |
break; |
default: |
// if existing check attributes before names are compared will safe performance |
if ((dir[dir_entry].Attribute & attribmask) != attribfilter) break; // attribute must match |
// then compare the name to the giveb dirname (first 11 characters include 8 chars of basename and 3 chars extension.) |
i = 0; |
while((i < 11) && (dir[dir_entry].Name[i] == dirname[i])) i++; |
if (i < 10) break; // names does not match |
// if dirname and attribute have matched |
file->Attribute = dir[dir_entry].Attribute; // store attribute of found dir entry |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[dir_entry].StartCluster); // set sector of first data cluster |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->DirectorySector = curr_sector; // current sector |
file->DirectoryIndex = dir_entry; // current direntry in current sector |
file->Size = dir[dir_entry].Size; |
direntry_exist = 1; // mark as found |
dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
} // end of first byte of name check |
} |
dir_sector++; // search next sector |
// stop if we reached the end of the cluster or the end of the root dir |
}while((dir_sector < max_dir_sector) && (!direntry_exist)); |
// if we are seaching in the data area and the file not found in this cluster so take next cluster. |
if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
{ |
end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
} |
}while((end_of_directory_not_reached) && (!direntry_exist)); // repeat until a next cluster exist an no |
return(direntry_exist); |
} |
/****************************************************************************************************************************************/ |
/* Function: CreateDirectoryEntry(int8_t *, uint16_t, File_t *) */ |
/* */ |
/* Description: This function looks for the next free position in the directory and creates an entry. */ |
/* The type of an directory entry is specified by the file attribute. */ |
/* */ |
/* Returnvalue: Return 0 on error */ |
/****************************************************************************************************************************************/ |
uint8_t CreateDirectoryEntry(int8_t *dirname, uint8_t attrib, File_t *file) |
{ |
uint32_t dir_sector, max_dir_sector, curr_sector; |
uint16_t dir_entry = 0; |
uint16_t subdircluster, dircluster = 0; |
uint16_t end_of_directory_not_reached = 0; |
uint8_t i = 0; |
uint8_t retvalue = 0; |
DirEntry_t* dir; |
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return (retvalue); |
// It is not checked here that the dir entry that should be created is already existent! |
// Dir entries can be created only in file-clusters that have |
// the dir-flag set in its attribute or within the root directory |
file->FirstSectorOfFirstCluster = 0; |
// no current directory exist therefore assume creating in the root |
if(file->DirectorySector == 0) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
dircluster = 0; |
file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector; |
} |
// within the root directory area we can read sectors sequentially until the end of this area |
else if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.FirstDataSector)) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
} |
// within the data clusters we can read sectors sequentially only within the cluster |
else if((Partition.FirstDataSector <= file->DirectorySector) && (file->DirectorySector <= Partition.LastDataSector)) |
{ |
max_dir_sector = Partition.SectorsPerCluster; |
} |
else return (retvalue); // bad sector range for directory sector of the file |
// if search area is not defined yet |
if(file->FirstSectorOfFirstCluster == 0) |
{ |
// check if the directory entry of current file is existent and has the dir-flag set |
file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
switch((uint8_t)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
return (retvalue); |
break; |
default: // and is a real directory |
if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY) |
{ // current file is not a directory therefore no file or subdirectory can be created here |
return (retvalue); |
} |
break; |
} |
dircluster = dir[file->DirectoryIndex].StartCluster; |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dircluster); |
} |
subdircluster = FindNextFreeCluster(file); // get the next free cluster on the disk and mark it as used. |
if(subdircluster) |
{ |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
do // loop over all clusters of current directory |
{ |
dir_sector = 0; // reset sector counter within a new cluster |
do // loop over all sectors of a cluster or all sectors of the root directory |
{ |
curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
// search all directory entries of a sector |
for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
{ // check if current direntry is available |
if(((uint8_t)dir[dir_entry].Name[0] == SLOT_EMPTY) || ((uint8_t)dir[dir_entry].Name[0] == SLOT_DELETED)) |
{ // a free direntry was found |
for(i = 0; i < 11; i++) dir[dir_entry].Name[i] = dirname[i]; // Set dir name |
dir[dir_entry].Attribute = attrib; // Set the attribute of the new directoryentry. |
dir[dir_entry].StartCluster = subdircluster; // copy the location of the first datacluster to the directoryentry. |
dir[dir_entry].DateTime = FileDateTime(&SystemTime); // set date/time |
dir[dir_entry].Size = 0; // the new createted file has no content yet. |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to card |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(subdircluster); // Calculate absolute sectorposition of first datacluster. |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; // Start reading the file with the first sector of the first datacluster. |
file->SectorOfCurrCluster = 0; // reset sector of cureen cluster |
file->ByteOfCurrSector = 0; // reset the byte location within the current sector |
file->Attribute = attrib; // set file attribute to dir attribute |
file->Size = 0; // new file has no size |
file->DirectorySector = curr_sector; |
file->DirectoryIndex = dir_entry; |
if((attrib & ATTR_SUBDIRECTORY) == ATTR_SUBDIRECTORY) // if a new directory was created then initilize the data area |
{ |
ClearCurrCluster(file); // fill cluster with zeros |
file->SectorInCache = file->FirstSectorOfFirstCluster; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; |
// create direntry "." to current dir |
dir[0].Name[0] = 0x2E; |
for(i = 1; i < 11; i++) dir[0].Name[i] = ' '; |
dir[0].Attribute = ATTR_SUBDIRECTORY; |
dir[0].StartCluster = subdircluster; |
dir[0].DateTime = 0; |
dir[0].Size = 0; |
// create direntry ".." to the upper dir |
dir[1].Name[0] = 0x2E; |
dir[1].Name[1] = 0x2E; |
for(i = 2; i < 11; i++) dir[1].Name[i] = ' '; |
dir[1].Attribute = ATTR_SUBDIRECTORY; |
dir[1].StartCluster = dircluster; |
dir[1].DateTime = 0; |
dir[1].Size = 0; |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
} |
retvalue = 1; |
dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
} |
} |
dir_sector++; // search next sector |
// stop if we reached the end of the cluster or the end of the root dir |
}while((dir_sector < max_dir_sector) && (!retvalue)); |
// if we are seaching in the data area and the file not found in this cluster so take next cluster. |
if(!retvalue && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
{ |
end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
} |
}while((end_of_directory_not_reached) && (!retvalue)); |
// Perhaps we are at the end of the last cluster of a directory file an have no free direntry found. |
// Then we would need to add a cluster to that file and create the new direntry there. |
// This code is not implemented yet, because its occurs only if more that 32*32=1024 direntries are |
// within a subdirectory of root. |
} |
return(retvalue); // return 1 if file has been created otherwise return 0. |
} |
/********************************************************************************************************************************************/ |
/* Function: FileExist(const int8_t* filename, uint8_t attribfilter, uint8_t attribmask, File_t *file); */ |
/* */ |
/* Description: This function looks for the specified file including its subdirectories beginning */ |
/* in the rootdirectory of the drive. If the file is found the Filepointer properties are */ |
/* updated. */ |
/* */ |
/* Returnvalue: 1 if file is found else 0. */ |
/********************************************************************************************************************************************/ |
uint8_t FileExist(const int8_t* filename, const uint8_t attribfilter, const uint8_t attribmask, File_t *file) |
{ |
int8_t* path = 0; |
int8_t* subpath = 0; |
uint8_t af, am, file_exist = 0; |
int8_t dirname[12]; // 8+3 + temination character |
// if incomming pointers are useless return immediatly |
if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return 0; |
// trace along the filepath |
path = (int8_t*)filename; // start a the beginning of the filename string |
file->DirectorySector = 0; // start at RootDirectory with file search |
file->DirectoryIndex = 0; |
// as long as the file was not found and the remaining path is not empty |
while((*path != 0) && !file_exist) |
{ // separate dirname and subpath from filepath string |
subpath = SeperateDirName(path, dirname); |
if(subpath != NULL) |
{ |
if(*subpath == 0) |
{ // empty subpath indicates last element of dir chain |
af = attribfilter; |
am = attribmask; |
} |
else // it must be a subdirectory and no volume label |
{ |
af = ATTR_SUBDIRECTORY; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; |
} |
if(!DirectoryEntryExist(dirname, af, am, file)) |
{ |
return (file_exist); // subdirectory does not exist |
} |
else |
{ |
if (*subpath == 0) |
{ |
file_exist = 1; // last element of path chain was found with the given attribute filter |
} |
} |
} |
else // error seperating the subpath |
{ |
return file_exist; // bad subdir format |
} |
path = subpath; |
subpath = 0; |
} |
return (file_exist); |
} |
/********************************************************************************************************************************************/ |
/* Function: FileCreate(const s8* filename, u8 attrib, File_t *file); */ |
/* */ |
/* Description: This function looks for the specified file including its subdirectories beginning */ |
/* in the rootdirectory of the partition. If the file is found the Filepointer properties are */ |
/* updated. If file or its subdirectories are not found they will be created */ |
/* */ |
/* Returnvalue: 1 if file was created else 0. */ |
/********************************************************************************************************************************************/ |
uint8_t FileCreate(const int8_t* filename, const uint8_t attrib, File_t *file) |
{ |
int8_t *path = 0; |
int8_t *subpath = 0; |
uint8_t af, am, file_created = 0; |
int8_t dirname[12]; |
// if incomming pointers are useless return immediatly |
if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return 0; |
// trace along the filepath |
path = (int8_t*)filename; // start a the beginning of the filename string |
file->DirectorySector = 0; // start at RootDirectory with file search |
file->DirectoryIndex = 0; |
// as long as the file was not created and the remaining file path is not empty |
while((*path != 0) && !file_created) |
{ // separate dirname and subpath from filepath string |
subpath = SeperateDirName(path, dirname); |
if(subpath != NULL) |
{ |
if(*subpath == 0) |
{ // empty subpath indicates last element of dir chain |
af = ATTR_NONE; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; // any file that is no subdir or volume label |
} |
else // it must be a subdirectory and no volume label |
{ |
af = ATTR_SUBDIRECTORY; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; |
} |
if(!DirectoryEntryExist(dirname, af, am, file)) // if subdir or file is not existent |
{ // try to create subdir or file |
if(*subpath == 0) af = attrib; // if last element in dir chain take the given attribute |
if(!CreateDirectoryEntry(dirname, af, file)) |
{ // could not be created |
return(file_created); |
} |
else if (*subpath == 0) file_created = 1; // last element of path chain was created |
} |
} |
else // error seperating the subpath |
{ |
return file_created; // bad subdir format |
} |
path = subpath; |
subpath = 0; |
} |
return (file_created); |
} |
/********************************************************************************************************************************************/ |
/* Function: File_t * fopen_(int8_t* filename, int8_t mode); */ |
/* */ |
/* Description: This function looks for the specified file in the rootdirectory of the drive. If the file is found the number of the */ |
/* corrosponding filepointer is returned. Only modes 'r' (reading) and 'a' append are implemented yet. */ |
/* */ |
/* Returnvalue: The filepointer to the file or 0 if faild. */ |
/********************************************************************************************************************************************/ |
File_t * fopen_(int8_t * const filename, const int8_t mode) |
{ |
File_t *file = 0; |
if((!Partition.IsValid) || (filename == 0)) return(file); |
// Look for an unused filepointer in the file pointer list? |
file = LockFilePointer(); |
// if no unused file pointer was found return 0 |
if(file == NULL) return(file); |
// now we have found a free filepointer and claimed it |
// so let initiate its property values |
file->FirstSectorOfFirstCluster = 0; // Sectorpointer to the first sector of the first datacluster of the file. |
file->FirstSectorOfCurrCluster = 0; // Pointer to the cluster which is edited at the moment. |
file->SectorOfCurrCluster = 0; // The sector which is edited at the moment (cluster_pointer + sector_index). |
file->ByteOfCurrSector = 0; // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index). |
file->Mode = mode; // mode of fileoperation (read,write) |
file->Size = 0; // the size of the opened file in bytes. |
file->Position = 0; // pointer to a byte within the file 0 < fileposition < filesize |
file->SectorInCache = 0; // the last sector read, wich is still in the sectorbuffer. |
file->DirectorySector = 0; // the sectorposition where the directoryentry has been made. |
file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
file->Attribute = 0; // the attribute of the file opened. |
// check if a real file (no directory) to the given filename exist |
if(FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file)) |
{ // file exist |
switch(mode) // check mode |
{ |
case 'a': // if mode is: append to file |
if((file->Attribute & ATTR_READONLY) == ATTR_READONLY) |
{ // file is marked as readonly --> do not open this file |
fclose_(file); |
file = NULL; |
} |
else |
{ // file is not marked as read only --> goto end of file |
fseek_(file, 0, SEEK_END); // point to the end of the file |
} |
break; |
case 'w': // if mode is: write to file |
if((file->Attribute & ATTR_READONLY) == ATTR_READONLY) |
{ // file is marked as readonly --> do not open this file |
fclose_(file); |
file = NULL; |
} |
else |
{ // file is not marked as read only --> goto start of file |
// free all clusters of that file |
DeleteClusterChain(SectorToFat16Cluster(file->FirstSectorOfFirstCluster)); |
// mar an empy cluster as the last one and store the corresponding sector |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(FindNextFreeCluster(file)); |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->Size = 0; |
file->Position = 0; |
fseek_(file, 0, SEEK_SET); |
} |
break; |
case 'r': // if mode is: read from file |
// goto end of file |
fseek_(file, 0, SEEK_SET); |
break; |
default: // other modes are not supported |
fclose_(file); |
file = NULL; |
break; |
} |
return(file); |
} |
else // file does not exist |
{ |
switch(mode) // check mode |
{ |
case 'a': |
case 'w': // if mode is write or append |
// try to create the file |
if(!FileCreate(filename, ATTR_ARCHIVE, file)) |
{ // if it could not be created |
fclose_(file); |
file = NULL; |
} |
break; |
case 'r': // else opened for 'r' |
default: // of unsupported mode |
fclose_(file); |
file = NULL; |
break; |
} |
return(file); |
} |
// we should never come to this point |
fclose_(file); |
file = NULL; |
return(file); |
} |
/****************************************************************************************************************************************************/ |
/* Function: fflush_(File *); */ |
/* */ |
/* Description: This function writes the data already in the buffer but not yet written to the file. */ |
/* */ |
/* Returnvalue: 0 on success EOF on error */ |
/****************************************************************************************************************************************************/ |
int16_t fflush_(File_t * const file) |
{ |
DirEntry_t *dir; |
if((file == NULL) || (!Partition.IsValid)) return (EOF); |
switch(file->Mode) |
{ |
case 'a': |
case 'w': |
if(file->ByteOfCurrSector > 0) // has data been added to the file? |
{ |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// save the data still in the buffer |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
} |
file->SectorInCache = file->DirectorySector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read the directory entry for this file. |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
dir = (DirEntry_t *)file->Cache; |
dir[file->DirectoryIndex].Size = file->Size; // update file size |
dir[file->DirectoryIndex].DateTime = FileDateTime(&SystemTime); // update date time |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to sd-card |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
break; |
case 'r': |
default: |
return(EOF); |
break; |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: fclose_(File *file); */ |
/* */ |
/* Description: This function closes the open file by writing the remaining data */ |
/* from the buffer to the device and entering the filesize in the directory entry. */ |
/* */ |
/* Returnvalue: 0 on success EOF on error */ |
/****************************************************************************************************************************************/ |
int16_t fclose_(File_t *file) |
{ |
int16_t returnvalue = EOF; |
if(file == NULL) return(returnvalue); |
returnvalue = fflush_(file); |
UnlockFilePointer(file); |
return(returnvalue); |
} |
/********************************************************************************************************************************************/ |
/* Function: fgetc_(File *file); */ |
/* */ |
/* Description: This function reads and returns one character from the specified file. Is the end of the actual sector reached the */ |
/* next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT. */ |
/* */ |
/* Returnvalue: The function returns the character read from the specified memorylocation as u8 casted to s16 or EOF. */ |
/********************************************************************************************************************************************/ |
int16_t fgetc_(File_t * const file) |
{ |
int16_t c = EOF; |
uint32_t curr_sector; |
if( (!Partition.IsValid) || (file == NULL)) return(c); |
// if the end of the file is not reached, get the next character. |
if((0 < file->Size) && ((file->Position+1) < file->Size) ) |
{ |
curr_sector = file->FirstSectorOfCurrCluster; // calculate the sector of the next character to be read. |
curr_sector += file->SectorOfCurrCluster; |
if(file->SectorInCache != curr_sector) |
{ |
file->SectorInCache = curr_sector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache,file->Cache)) |
{ |
Fat16_Deinit(); |
return(c); |
} |
} |
c = (int16_t) file->Cache[file->ByteOfCurrSector]; |
file->Position++; // increment file position |
file->ByteOfCurrSector++; // goto next byte in sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) // if end of sector |
{ |
file->ByteOfCurrSector = 0; // reset byte location |
file->SectorOfCurrCluster++; // next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster) // if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ |
file->SectorOfCurrCluster = 0; // start reading new cluster at first sector of the cluster. |
} |
else // the last cluster was allready reached |
{ |
file->SectorOfCurrCluster--; // jump back to the last sector in the last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set ByteOfCurrSector one byte over sector end |
} |
} |
} |
} |
return(c); |
} |
/********************************************************************************************************************************************/ |
/* Function: fputc_( const s8 c, File *file); */ |
/* */ |
/* Description: This function writes a byte to the specified file and takes care of writing the necessary FAT- Entries. */ |
/* next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT. */ |
/* */ |
/* Returnvalue: The function returns the character written to the stream or EOF on error. */ |
/********************************************************************************************************************************************/ |
int16_t fputc_(const int8_t c, File_t * const file) |
{ |
uint32_t curr_sector = 0; |
if((!Partition.IsValid) || (file == NULL)) return(EOF); |
// If file position equals to file size, then the end of file has reached. |
// In this chase it has to be checked that the ByteOfCurrSector is BYTES_PER_SECTOR |
// and a new cluster should be appended. |
if((file->Position >= file->Size) && (file->ByteOfCurrSector >= BYTES_PER_SECTOR)) |
{ |
if(!AppendCluster(file)) return(EOF); |
} |
curr_sector = file->FirstSectorOfCurrCluster; |
curr_sector += file->SectorOfCurrCluster; |
if(file->SectorInCache != curr_sector) |
{ |
file->SectorInCache = curr_sector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
} |
file->Cache[file->ByteOfCurrSector] = (uint8_t)c; // write databyte into the buffer. The byte will be written to the device at once |
if(file->Size == file->Position) file->Size++; // a character has been written to the file so the size is incremented only when the character has been added at the end of the file. |
file->Position++; // the actual positon within the file. |
file->ByteOfCurrSector++; // goto next byte in sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) // if the end of this sector is reached yet |
{ // save the sector to the sd-card |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
file->ByteOfCurrSector = 0; // reset byte location |
file->SectorOfCurrCluster++; // next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster)// if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(!GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ // if current cluster was the last cluster of the file |
if(!AppendCluster(file)) // append a new and free cluster at the end of the file. |
{ |
file->SectorOfCurrCluster--; // jump back to last sector of last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set byte location to 1 byte over sector len |
return(EOF); |
} |
} |
else // next cluster |
{ |
file->SectorOfCurrCluster = 0; // start reading new cluster at first sector of the cluster. |
} |
} |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: fread_(void *buffer, uint32_t size, uint32_t count, File *File); */ |
/* */ |
/* Description: This function reads count objects of the specified size */ |
/* from the actual position of the file to the specified buffer. */ |
/* */ |
/* Returnvalue: The function returns the number of objects (not bytes) read from the file. */ |
/****************************************************************************************************************************************/ |
uint32_t fread_(void * const buffer, uint32_t size, uint32_t count, File_t * const file) |
{ |
uint32_t object_cnt = 0; // count the number of objects read from the file. |
uint32_t object_size = 0; // count the number of bytes read from the actual object. |
uint8_t *pbuff = 0; // a pointer to the actual bufferposition. |
uint8_t success = 1; // no error occured during read operation to the file. |
int16_t c; |
if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
pbuff = (uint8_t *) buffer; // cast the void pointer to an u8 * |
while((object_cnt < count) && success) |
{ |
object_size = size; |
while((size > 0) && success) |
{ |
c = fgetc_(file); |
if(c != EOF) |
{ |
*pbuff = (uint8_t)c; // read a byte from the buffer to the opened file. |
pbuff++; |
size--; |
} |
else // error or end of file reached |
{ |
success = 0; |
} |
} |
if(success) object_cnt++; |
} |
return(object_cnt); // return the number of objects succesfully read from the file |
} |
/****************************************************************************************************************************************/ |
/* Function: fwrite_(void *buffer, uint32_t size, uint32_t count, File *file); */ |
/* */ |
/* Description: This function writes count objects of the specified size */ |
/* from the buffer pointer to the actual position in the file. */ |
/* */ |
/* Returnvalue: The function returns the number of objects (not bytes) read from the file. */ |
/****************************************************************************************************************************************/ |
uint32_t fwrite_(void * const buffer, uint32_t size, uint32_t count, File_t * const file) |
{ |
uint32_t object_cnt = 0; // count the number of objects written to the file. |
uint32_t object_size = 0; // count the number of bytes written from the actual object. |
uint8_t *pbuff = 0; // a pointer to the actual bufferposition. |
uint8_t success = 1; // no error occured during write operation to the file. |
int16_t c; |
if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
pbuff = (uint8_t *) buffer; // cast the void pointer to an u8 * |
while((object_cnt < count) && success) |
{ |
object_size = size; |
while((size > 0) && success) |
{ |
c = fputc_(*pbuff, file); // write a byte from the buffer to the opened file. |
if(c != EOF) |
{ |
pbuff++; |
size--; |
} |
else |
{ |
success = 0; |
} |
} |
if(success) object_cnt++; |
} |
return(object_cnt); // return the number of objects succesfully written to the file |
} |
/****************************************************************************************************************************************/ |
/* Function: fputs_(const int8_t *string, File_t *File); */ |
/* */ |
/* Description: This function writes a string to the specified file. */ |
/* */ |
/* Returnvalue: The function returns a no negative value or EOF on error. */ |
/****************************************************************************************************************************************/ |
int16_t fputs_(int8_t * const string, File_t * const file) |
{ |
uint8_t i=0; |
int16_t c = 0; |
if((!Partition.IsValid) || (file == NULL) || (string == NULL)) return(0); |
while((string[i] != 0)&& (c != EOF)) |
{ |
c = fputc_(string[i], file); |
i++; |
} |
return(c); |
} |
/****************************************************************************************************************************************/ |
/* Function: fgets_(int8 *, int16_t , File_t *); */ |
/* */ |
/* Description: This function reads a string from the file to the specifies string. */ |
/* */ |
/* Returnvalue: A pointer to the string read from the file or 0 on error. */ |
/****************************************************************************************************************************************/ |
int8_t * fgets_(int8_t * const string, int16_t length, File_t * const file) |
{ |
int8_t *pbuff; |
int16_t c = 0, bytecount; |
if((!Partition.IsValid) || (file == NULL) || (string == NULL) || (length > 1)) return (0); |
pbuff = string; |
bytecount = length; |
while(bytecount > 1) // read the count-1 characters from the file to the string. |
{ |
c = fgetc_(file); // read a character from the opened file. |
switch(c) |
{ |
case 0x0A: |
*pbuff = 0; // set string terminator |
return(string); // stop loop |
break; |
case EOF: |
*pbuff = 0; // set string terminator |
return(0); |
default: |
*pbuff++ = (int8_t)c; // copy byte to string |
bytecount--; |
break; |
} |
} |
*pbuff = 0; |
return(string); |
} |
/****************************************************************************************************************************************/ |
/* Function: fexist_(const int8_t*); */ |
/* */ |
/* Description: This function checks if a file already exist. */ |
/* */ |
/* Returnvalue: 1 if the file exist else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t fexist_(int8_t* const filename) |
{ |
uint8_t exist = 0; |
File_t *file = 0; |
file = LockFilePointer(); |
exist = FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file); |
UnlockFilePointer(file); |
return(exist); |
} |
/****************************************************************************************************************************************/ |
/* Function: feof_(File_t *File); */ |
/* */ |
/* Description: This function checks wether the end of the file has been reached. */ |
/* */ |
/* Returnvalue: 0 if the end of the file was not reached otherwise 1. */ |
/****************************************************************************************************************************************/ |
uint8_t feof_(File_t *file) |
{ |
if(((file->Position)+1) < (file->Size)) |
{ |
return(0); |
} |
else |
{ |
return(1); |
} |
} |
/FollowMe/fat16.h |
---|
0,0 → 1,70 |
#ifndef _FAT16_H |
#define _FAT16_H |
//________________________________________________________________________________________________________________________________________ |
// |
// Definitions |
// |
//________________________________________________________________________________________________________________________________________ |
//#define __USE_TIME_DATE_ATTRIBUTE |
#define FILE_MAX_OPEN 3 // The number of files that can accessed simultaneously. |
#define SEEK_SET 0 |
#define SEEK_CUR 1 |
#define SEEK_END 2 |
#define EOF (-1) |
#define BYTES_PER_SECTOR 512 |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of a filepointer |
________________________________________________________________________________________________________________________________________ |
*/ |
typedef struct |
{ |
uint32_t FirstSectorOfFirstCluster; // First sector of the first cluster of the file. |
uint32_t FirstSectorOfCurrCluster; // First sector of the cluster which is edited at the moment. |
uint8_t SectorOfCurrCluster; // The sector within the current cluster. |
uint16_t ByteOfCurrSector; // The byte location within the current sector. |
uint8_t Mode; // Mode of fileoperation (read,write) |
uint32_t Size; // The size of the opend file in bytes. |
uint32_t Position; // Pointer to a character within the file 0 < fileposition < filesize |
uint32_t DirectorySector; // the sectorposition where the directoryentry has been made. |
uint16_t DirectoryIndex; // The index to the directoryentry within the specified sector. |
uint8_t Attribute; // The attribute of the file opened. |
uint8_t Cache[BYTES_PER_SECTOR]; // Cache for read and write operation from or to the sd-card. |
uint32_t SectorInCache; // The last sector read, which is still in the sector cache. |
uint8_t State; // State of the filepointer (used/unused/...) |
} File_t; |
//________________________________________________________________________________________________________________________________________ |
// |
// API to the FAT16 filesystem |
// |
//________________________________________________________________________________________________________________________________________ |
extern uint8_t Fat16_Init(void); |
extern uint8_t Fat16_Deinit(void); |
extern uint8_t Fat16_IsValid(void); |
extern File_t * fopen_(int8_t * const filename, const int8_t mode); |
extern int16_t fclose_(File_t *file); |
extern uint8_t fexist_(int8_t * const filename); |
extern int16_t fflush_(File_t * const file); |
extern int16_t fseek_(File_t * const file, int32_t offset, int16_t origin); |
extern int16_t fgetc_(File_t * const file); |
extern int16_t fputc_(const int8_t c, File_t * const file); |
extern uint32_t fread_(void * const buffer, uint32_t size, uint32_t count, File_t * const file); |
extern uint32_t fwrite_(void *buffer, uint32_t size, uint32_t count, File_t *file); |
extern int16_t fputs_(int8_t * const string, File_t * const file); |
extern int8_t * fgets_(int8_t * const string, const int16_t length, File_t * const file); |
extern uint8_t feof_(File_t * const file); |
#endif //_FAT16_H |
/FollowMe/gps.c |
---|
0,0 → 1,98 |
#include "gps.h" |
#include "uart0.h" |
#include "main.h" |
#include "timer0.h" |
#define GPS_TIMEOUT 1000 // of ne new gps data arrivw within that time an error ist set |
#define GPS_MINSATS 4 |
//------------------------------------------------------------ |
// copy GPS position from source position to target position |
uint8_t GPS_CopyPosition(GPS_Pos_t * pGPSPosSrc, GPS_Pos_t* pGPSPosTgt) |
{ |
uint8_t retval = 0; |
if((pGPSPosSrc == 0) || (pGPSPosTgt == 0)) return(retval); // bad pointer |
// copy only valid positions |
if(pGPSPosSrc->Status != INVALID) |
{ |
// if the source GPS position is not invalid |
pGPSPosTgt->Longitude = pGPSPosSrc->Longitude; |
pGPSPosTgt->Latitude = pGPSPosSrc->Latitude; |
pGPSPosTgt->Altitude = pGPSPosSrc->Altitude; |
pGPSPosTgt->Status = NEWDATA; // mark data in target position as new |
retval = 1; |
} |
return(retval); |
} |
//------------------------------------------------------------ |
// clear position data |
uint8_t GPS_ClearPosition(GPS_Pos_t * pGPSPos) |
{ |
uint8_t retval = 0; |
if(pGPSPos == 0) return(retval); // bad pointer |
else |
{ |
pGPSPos->Longitude = 0; |
pGPSPos->Latitude = 0; |
pGPSPos->Altitude = 0; |
pGPSPos->Status = INVALID; |
retval = 1; |
} |
return (retval); |
} |
//------------------------------------------------------------ |
// check for new GPS data |
void GPS_Update(void) |
{ |
static uint16_t GPS_Timeout = 0; |
static uint16_t beep_rythm = 0; |
switch(GPSData.Status) |
{ |
case INVALID: |
Error |= ERROR_GPS_RX_TIMEOUT; |
GPS_ClearPosition(&(FollowMe.Position)); // clear followme position |
break; |
case PROCESSED: |
// wait for timeout |
if(CheckDelay(GPS_Timeout)) |
{ |
GPSData.Status = INVALID; |
} |
break; |
case NEWDATA: |
GPS_Timeout = SetDelay(GPS_TIMEOUT); // reset gps timeout |
Error &= ~ERROR_GPS_RX_TIMEOUT; // clear possible error |
beep_rythm++; |
// update data in the follow me message |
if((GPSData.SatFix & SATFIX_3D) && (GPSData.NumOfSats >= GPS_MINSATS)) |
{ |
GPS_CopyPosition(&(GPSData.Position),&(FollowMe.Position)); |
} |
else |
{ |
GPS_ClearPosition(&(FollowMe.Position)); // clear followme position |
} |
// NC like sound on bad gps signals |
if(FollowMe_active) |
{ |
if(!(GPSData.Flags & FLAG_GPSFIXOK) && !(beep_rythm % 5)) BeepTime = 100; |
else if ((GPSData.NumOfSats < GPS_MINSATS) && !(beep_rythm % 5)) BeepTime = 10; |
} |
GPSData.Status = PROCESSED; // set to processed unlocks the buffer for new data |
break; |
default: |
GPSData.Status = INVALID; |
break; |
} |
} |
/FollowMe/gps.h |
---|
0,0 → 1,7 |
#ifndef _GPS_H |
#define _GPS_H |
#include "ubx.h" |
extern void GPS_Update(void); |
#endif //_GPS_H |
/FollowMe/led.c |
---|
0,0 → 1,23 |
#include <inttypes.h> |
#include "led.h" |
// initializes the LED control outputs |
void LED_Init(void) |
{ |
#ifdef USE_SDLOGGER |
// set PB0 as output (control of red LED) |
DDRB |= (1<<DDB0); |
LEDRED_OFF; |
#endif |
#ifdef USE_FOLLOWME |
// set PB0 as output (control of green LED) |
DDRB |= (1<<DDB0); |
LEDGRN_OFF; |
// set PB1 as output (control of red LED) |
DDRB |= (1<<DDB1); |
LEDRED_OFF; |
#endif |
} |
/FollowMe/led.h |
---|
0,0 → 1,29 |
#ifndef _LED_H |
#define _LED_H |
#include <avr/io.h> |
#ifdef USE_SDLOGGER |
#define LEDRED_OFF PORTB |= (1<<PORTB0) |
#define LEDRED_ON PORTB &= ~(1<<PORTB0) |
#define LEDRED_TOGGLE PORTB ^= (1<<PORTB0) |
#define LEDGRN_OFF |
#define LEDGRN_ON |
#define LEDGRN_TOGGLE |
#endif |
#ifdef USE_FOLLOWME |
#define LEDGRN_OFF PORTB |= (1<<PORTB1) |
#define LEDGRN_ON PORTB &= ~(1<<PORTB1) |
#define LEDGRN_TOGGLE PORTB ^= (1<<PORTB1) |
#define LEDRED_OFF PORTB |= (1<<PORTB0) |
#define LEDRED_ON PORTB &= ~(1<<PORTB0) |
#define LEDRED_TOGGLE PORTB ^= (1<<PORTB0) |
#endif |
void LED_Init(void); |
#endif //_LED_H |
/FollowMe/main.c |
---|
0,0 → 1,230 |
#include <avr/boot.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "main.h" |
#include "timer0.h" |
#include "uart0.h" |
#include "uart1.h" |
#include "fat16.h" |
#include "led.h" |
#include "menu.h" |
#include "printf_P.h" |
#include "analog.h" |
#include "gps.h" |
#include "button.h" |
#define FOLLOWME_INTERVAL 1000 // 1 second update |
#define CELLUNDERVOLTAGE 32 // lowest allowed voltage/cell; 32 = 3.2V |
#ifdef USE_FOLLOWME |
int16_t UBat = 120; |
int16_t Zellenzahl = 0; |
int16_t PowerOn = 0; |
int16_t i = 0; |
int16_t delay = 0; |
int16_t FollowMe_active = 0; |
#endif |
uint16_t Error = 0; |
typedef enum |
{ |
STATE_UNDEFINED, |
STATE_IDLE, |
STATE_SEND_FOLLOWME |
} SysState_t; |
int main (void) |
{ |
static uint16_t FollowMe_Timer = 0; |
static SysState_t SysState = STATE_UNDEFINED; |
// disable interrupts global |
cli(); |
// disable watchdog |
MCUSR &=~(1<<WDRF); |
WDTCSR |= (1<<WDCE)|(1<<WDE); |
WDTCSR = 0; |
// initalize modules |
LED_Init(); |
LEDRED_ON; |
TIMER0_Init(); |
USART0_Init(); |
UBX_Init(); |
USART1_Init(); |
ADC_Init(); |
Button_Init(); |
// enable interrupts global |
sei(); |
Fat16_Init(); |
LEDRED_OFF; |
LEDGRN_ON; |
// try to initialize the FAT 16 filesystem on the SD-Card |
Fat16_Init(); |
#ifdef USE_SDLOGGER |
printf("\r\n\r\nHW: SD-Logger"); |
#endif |
#ifdef USE_FOLLOWME |
printf("\r\n\r\nHW: Follow-Me"); |
#endif |
printf("\r\nFollow Me\n\rSoftware:V%d.%d%c ",VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH + 'a'); |
printf("\r\n------------------------------"); |
printf("\r\n"); |
//BeepTime = 2000; |
LCD_Clear(); |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL); |
while (1) |
{ |
// get gps data to update the follow me position |
GPS_Update(); |
// check for button action and change state resectively |
if(GetButton()) |
{ |
BeepTime = 200; |
switch(SysState) |
{ |
case STATE_IDLE: |
if(!Error) SysState = STATE_SEND_FOLLOWME; // activate followme only of no error has occured |
break; |
case STATE_SEND_FOLLOWME: |
SysState = STATE_IDLE; |
break; |
default: |
SysState = STATE_IDLE; |
break; |
} |
} |
// state machine |
switch(SysState) |
{ |
case STATE_SEND_FOLLOWME: |
if(CheckDelay(FollowMe_Timer)) // time for next message? |
{ |
if(FollowMe.Position.Status == NEWDATA) // if new |
{ // update remaining data |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL); // reset timer |
FollowMe.Heading = -1; // invalid heading |
FollowMe.ToleranceRadius = 1; // 1 meter |
FollowMe.HoldTime = 60; // go home after 60s without any update |
FollowMe.Event_Flag = 0; // no event |
FollowMe.reserve[0] = 0; // reserve |
FollowMe.reserve[1] = 0; // reserve |
FollowMe.reserve[2] = 0; // reserve |
FollowMe.reserve[3] = 0; // reserve |
Request_SendFollowMe = 1; // triggers serial tranmission |
} |
else // now new position avalable (maybe bad gps signal condition) |
{ |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL/4); // reset timer on higer frequency |
} |
LEDGRN_TOGGLE; // indication of active follow me |
FollowMe_active = 1; |
} |
break; |
case STATE_IDLE: |
// do nothing |
LEDGRN_ON; |
FollowMe_active = 0; |
break; |
default: |
// triger to idle state |
SysState = STATE_IDLE; |
break; |
} |
// restart ADConversion if ready |
if(ADReady) |
{ |
DebugOut.Analog[0] = Adc0; |
DebugOut.Analog[1] = Adc1; |
DebugOut.Analog[2] = Adc2; |
DebugOut.Analog[3] = Adc3; |
DebugOut.Analog[4] = Adc4; |
DebugOut.Analog[5] = Adc5; |
DebugOut.Analog[6] = Adc6; |
DebugOut.Analog[7] = Adc7; |
#ifdef USE_FOLLOWME |
// AVcc = 5V --> 5V = 1024 counts |
// the voltage at the voltage divider reference point is 0.8V less that the UBat |
// because of the silicon diode inbetween. |
// voltage divider R2=10K, R3=3K9 |
// UAdc4 = R3/(R3+R2)*UBat= 3.9/(3.9+10)*UBat = UBat/3.564 |
UBat = (3 * UBat + (64 * Adc4) / 368) / 4; |
DebugOut.Analog[8] = UBat; |
// check for zellenzahl |
if(PowerOn < 100) |
{ |
if(UBat<=84) Zellenzahl = 2; |
else Zellenzahl = 3; |
PowerOn++; |
} |
DebugOut.Analog[16] = Zellenzahl; |
DebugOut.Analog[17] = PowerOn; |
//show recognised Zellenzahl to user |
if(i < Zellenzahl && PowerOn >= 100 && BeepTime == 0 && delay > 1000) |
{ |
BeepTime = 100; |
i++; |
delay = 0; |
} |
if(delay < 1500) delay++; |
// monitor battery undervoltage [...||(UBat<74) as temporary workaround to protect 2s lipo packs] |
if(((UBat < Zellenzahl * CELLUNDERVOLTAGE)||(UBat < 74)) && (PowerOn >= 100)) |
{ // sound for low battery |
BeepModulation = 0x0300; |
if(!BeepTime) |
{ |
BeepTime = 6000; // 0.6 seconds |
} |
Error |= ERROR_LOW_BAT; |
} |
else |
{ |
Error &= ~ERROR_LOW_BAT; |
} |
#endif |
ADReady = 0; |
ADC_Enable(); // restart ad conversion sequence |
} |
// serial communication |
USART0_ProcessRxData(); |
USART0_TransmitTxData(); |
// indicate error, blinking code tbd. |
if(Error) LEDRED_ON; |
else LEDRED_OFF; |
} |
return (1); |
} |
/FollowMe/main.h |
---|
0,0 → 1,21 |
#ifndef _MAIN_H |
#define _MAIN_H |
#include <avr/io.h> |
#define SYSCLK F_CPU |
#define ERROR_GPS_RX_TIMEOUT 0x0001 |
#define ERROR_LOW_BAT 0x0002 |
extern uint16_t Error; |
extern int16_t FollowMe_active; |
#endif //_MAIN_H |
/FollowMe/makefile |
---|
0,0 → 1,430 |
#-------------------------------------------------------------------- |
# MCU name |
MCU = atmega644p |
F_CPU = 20000000 |
#------------------------------------------------------------------- |
VERSION_MAJOR = 0 |
VERSION_MINOR = 1 |
VERSION_PATCH = 2 |
VERSION_SERIAL_MAJOR = 10 # Serial Protocol Major Version |
VERSION_SERIAL_MINOR = 0 # Serial Protocol Minor Version |
#------------------------------------------------------------------- |
#OPTIONS |
# Use one of the motor setups |
BOARD = FOLLOWME |
#BOARD = SDLOGGER |
#------------------------------------------------------------------- |
# get SVN revision |
REV := $(shell sh -c "cat .svn/entries | sed -n '4p'") |
ifeq ($(MCU), atmega644p) |
FUSE_SETTINGS = -u -U lfuse:w:0xff:m -U hfuse:w:0xdf:m |
HEX_NAME = MEGA644p_$(BOARD) |
endif |
ifeq ($(F_CPU), 20000000) |
QUARZ = 20MHZ |
endif |
# Output format. (can be srec, ihex, binary) |
FORMAT = ihex |
# Target file name (without extension). |
ifeq ($(VERSION_PATCH), 0) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)a_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 1) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)b_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 2) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)c_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 3) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)d_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 4) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)e_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 5) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)f_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 6) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)g_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 7) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)h_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 8) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)i_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 9) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)j_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 10) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)k_SVN$(REV) |
endif |
# Optimization level, can be [0, 1, 2, 3, s]. 0 turns off optimization. |
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.) |
OPT = 2 |
########################################################################################################## |
# List C source files here. (C dependencies are automatically generated.) |
SRC = main.c uart0.c uart1.c printf_P.c timer0.c menu.c led.c ubx.c analog.c button.c crc16.c ssc.c sdc.c fat16.c gps.c |
########################################################################################################## |
# List Assembler source files here. |
# Make them always end in a capital .S. Files ending in a lowercase .s |
# will not be considered source files but generated files (assembler |
# output from the compiler), and will be deleted upon "make clean"! |
# Even though the DOS/Win* filesystem matches both .s and .S the same, |
# it will preserve the spelling of the filenames, and gcc itself does |
# care about how the name is spelled on its command-line. |
ASRC = |
# List any extra directories to look for include files here. |
# Each directory must be seperated by a space. |
EXTRAINCDIRS = |
# Optional compiler flags. |
# -g: generate debugging information (for GDB, or for COFF conversion) |
# -O*: optimization level |
# -f...: tuning, see gcc manual and avr-libc documentation |
# -Wall...: warning level |
# -Wa,...: tell GCC to pass this to the assembler. |
# -ahlms: create assembler listing |
CFLAGS = -O$(OPT) \ |
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \ |
-Wall -Wstrict-prototypes \ |
-Wa,-adhlns=$(<:.c=.lst) \ |
$(patsubst %,-I%,$(EXTRAINCDIRS)) |
# Set a "language standard" compiler flag. |
# Unremark just one line below to set the language standard to use. |
# gnu99 = C99 + GNU extensions. See GCC manual for more information. |
#CFLAGS += -std=c89 |
#CFLAGS += -std=gnu89 |
#CFLAGS += -std=c99 |
CFLAGS += -std=gnu99 |
CFLAGS += -DF_CPU=$(F_CPU) -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_PATCH=$(VERSION_PATCH) -DVERSION_SERIAL_MAJOR=$(VERSION_SERIAL_MAJOR) -DVERSION_SERIAL_MINOR=$(VERSION_SERIAL_MINOR) |
ifeq ($(BOARD), FOLLOWME) |
CFLAGS += -DUSE_FOLLOWME |
endif |
ifeq ($(BOARD), SDLOGGER) |
CFLAGS += -DUSE_SDLOGGER |
endif |
# Optional assembler flags. |
# -Wa,...: tell GCC to pass this to the assembler. |
# -ahlms: create listing |
# -gstabs: have the assembler create line number information; note that |
# for use in COFF files, additional information about filenames |
# and function names needs to be present in the assembler source |
# files -- see avr-libc docs [FIXME: not yet described there] |
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs |
# Optional linker flags. |
# -Wl,...: tell GCC to pass this to linker. |
# -Map: create map file |
# --cref: add cross reference to map file |
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref |
# Additional libraries |
# Minimalistic printf version |
#LDFLAGS += -Wl,-u,vfprintf -lprintf_min |
# Floating point printf version (requires -lm below) |
#LDFLAGS += -Wl,-u,vfprintf -lprintf_flt |
# -lm = math library |
LDFLAGS += -lm |
##LDFLAGS += -T./linkerfile/avr5.x |
# Programming support using avrdude. Settings and variables. |
# Programming hardware: alf avr910 avrisp bascom bsd |
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500 |
# |
# Type: avrdude -c ? |
# to get a full listing. |
# |
#AVRDUDE_PROGRAMMER = dt006 |
#AVRDUDE_PROGRAMMER = stk200 |
#AVRDUDE_PROGRAMMER = ponyser |
AVRDUDE_PROGRAMMER = avrispv2 |
#falls Ponyser ausgewählt wird, muss sich unsere avrdude-Configdatei im Bin-Verzeichnis des Compilers befinden |
#AVRDUDE_PORT = com1 # programmer connected to serial device |
#AVRDUDE_PORT = lpt1 # programmer connected to parallel port |
AVRDUDE_PORT = usb # programmer connected to USB |
#AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex |
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex $(FUSE_SETTINGS) |
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep |
#avrdude -c avrispv2 -P usb -p m32 -U flash:w:blink.hex |
AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) |
# Uncomment the following if you want avrdude's erase cycle counter. |
# Note that this counter needs to be initialized first using -Yn, |
# see avrdude manual. |
#AVRDUDE_ERASE += -y |
# Uncomment the following if you do /not/ wish a verification to be |
# performed after programming the device. |
AVRDUDE_FLAGS += -V |
# Increase verbosity level. Please use this when submitting bug |
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude> |
# to submit bug reports. |
#AVRDUDE_FLAGS += -v -v |
# --------------------------------------------------------------------------- |
# Define directories, if needed. |
DIRAVR = c:/winavr |
DIRAVRBIN = $(DIRAVR)/bin |
DIRAVRUTILS = $(DIRAVR)/utils/bin |
DIRINC = . |
DIRLIB = $(DIRAVR)/avr/lib |
# Define programs and commands. |
SHELL = sh |
CC = avr-gcc |
OBJCOPY = avr-objcopy |
OBJDUMP = avr-objdump |
SIZE = avr-size |
# Programming support using avrdude. |
AVRDUDE = avrdude |
REMOVE = rm -f |
COPY = cp |
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex |
ELFSIZE = $(SIZE) -A $(TARGET).elf |
# Define Messages |
# English |
MSG_ERRORS_NONE = Errors: none |
MSG_BEGIN = -------- begin -------- |
MSG_END = -------- end -------- |
MSG_SIZE_BEFORE = Size before: |
MSG_SIZE_AFTER = Size after: |
MSG_COFF = Converting to AVR COFF: |
MSG_EXTENDED_COFF = Converting to AVR Extended COFF: |
MSG_FLASH = Creating load file for Flash: |
MSG_EEPROM = Creating load file for EEPROM: |
MSG_EXTENDED_LISTING = Creating Extended Listing: |
MSG_SYMBOL_TABLE = Creating Symbol Table: |
MSG_LINKING = Linking: |
MSG_COMPILING = Compiling: |
MSG_ASSEMBLING = Assembling: |
MSG_CLEANING = Cleaning project: |
# Define all object files. |
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) |
# Define all listing files. |
LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) |
# Combine all necessary flags and optional flags. |
# Add target processor to flags. |
#ALL_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -I. $(CFLAGS) |
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) |
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) |
# Default target. |
all: begin gccversion sizebefore $(TARGET).elf $(TARGET).hex $(TARGET).eep \ |
$(TARGET).lss $(TARGET).sym sizeafter finished end |
# Eye candy. |
# AVR Studio 3.x does not check make's exit code but relies on |
# the following magic strings to be generated by the compile job. |
begin: |
@echo |
@echo $(MSG_BEGIN) |
finished: |
@echo $(MSG_ERRORS_NONE) |
end: |
@echo $(MSG_END) |
@echo |
# Display size of file. |
sizebefore: |
@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi |
sizeafter: |
@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi |
# Display compiler version information. |
gccversion : |
@$(CC) --version |
# Convert ELF to COFF for use in debugging / simulating in |
# AVR Studio or VMLAB. |
COFFCONVERT=$(OBJCOPY) --debugging \ |
--change-section-address .data-0x800000 \ |
--change-section-address .bss-0x800000 \ |
--change-section-address .noinit-0x800000 \ |
--change-section-address .eeprom-0x810000 |
coff: $(TARGET).elf |
@echo |
@echo $(MSG_COFF) $(TARGET).cof |
$(COFFCONVERT) -O coff-avr $< $(TARGET).cof |
extcoff: $(TARGET).elf |
@echo |
@echo $(MSG_EXTENDED_COFF) $(TARGET).cof |
$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof |
# Program the device. |
program: $(TARGET).hex $(TARGET).eep |
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) |
# Create final output files (.hex, .eep) from ELF output file. |
%.hex: %.elf |
@echo |
@echo $(MSG_FLASH) $@ |
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ |
%.eep: %.elf |
@echo |
@echo $(MSG_EEPROM) $@ |
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ |
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@ |
# Create extended listing file from ELF output file. |
%.lss: %.elf |
@echo |
@echo $(MSG_EXTENDED_LISTING) $@ |
$(OBJDUMP) -h -S $< > $@ |
# Create a symbol table from ELF output file. |
%.sym: %.elf |
@echo |
@echo $(MSG_SYMBOL_TABLE) $@ |
avr-nm -n $< > $@ |
# Link: create ELF output file from object files. |
.SECONDARY : $(TARGET).elf |
.PRECIOUS : $(OBJ) |
%.elf: $(OBJ) |
@echo |
@echo $(MSG_LINKING) $@ |
$(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) |
# Compile: create object files from C source files. |
%.o : %.c |
@echo |
@echo $(MSG_COMPILING) $< |
$(CC) -c $(ALL_CFLAGS) $< -o $@ |
# Compile: create assembler files from C source files. |
%.s : %.c |
$(CC) -S $(ALL_CFLAGS) $< -o $@ |
# Assemble: create object files from assembler source files. |
%.o : %.S |
@echo |
@echo $(MSG_ASSEMBLING) $< |
$(CC) -c $(ALL_ASFLAGS) $< -o $@ |
# Target: clean project. |
clean: begin clean_list finished end |
clean_list : |
@echo |
@echo $(MSG_CLEANING) |
# $(REMOVE) $(TARGET).hex |
$(REMOVE) $(TARGET).eep |
$(REMOVE) $(TARGET).obj |
$(REMOVE) $(TARGET).cof |
$(REMOVE) $(TARGET).elf |
$(REMOVE) $(TARGET).map |
$(REMOVE) $(TARGET).obj |
$(REMOVE) $(TARGET).a90 |
$(REMOVE) $(TARGET).sym |
$(REMOVE) $(TARGET).lnk |
$(REMOVE) $(TARGET).lss |
$(REMOVE) $(OBJ) |
$(REMOVE) $(LST) |
$(REMOVE) $(SRC:.c=.s) |
$(REMOVE) $(SRC:.c=.d) |
# Automatically generate C source code dependencies. |
# (Code originally taken from the GNU make user manual and modified |
# (See README.txt Credits).) |
# |
# Note that this will work with sh (bash) and sed that is shipped with WinAVR |
# (see the SHELL variable defined above). |
# This may not work with other shells or other seds. |
# |
%.d: %.c |
set -e; $(CC) -MM $(ALL_CFLAGS) $< \ |
| sed 's,\(.*\)\.o[ :]*,\1.o \1.d : ,g' > $@; \ |
[ -s $@ ] || rm -f $@ |
# Remove the '-' if you want to see the dependency files generated. |
-include $(SRC:.c=.d) |
# Listing of phony targets. |
.PHONY : all begin finish end sizebefore sizeafter gccversion coff extcoff \ |
clean clean_list program |
/FollowMe/menu.c |
---|
0,0 → 1,184 |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 04.2007 Holger Buss |
// + only for non-profit use |
// + www.MikroKopter.com |
// + see the File "License.txt" for further Informations |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdlib.h> |
#include <inttypes.h> |
#include "uart0.h" |
#include "printf_P.h" |
#include "ubx.h" |
#include "timer0.h" |
uint8_t MaxMenuItem = 3; |
uint8_t MenuItem = 0; |
uint8_t RemoteKeys = 0; |
#define KEY1 0x01 |
#define KEY2 0x02 |
#define KEY3 0x04 |
#define KEY4 0x08 |
#define KEY5 0x10 |
#define DISPLAYBUFFSIZE 80 |
int8_t DisplayBuff[DISPLAYBUFFSIZE] = "Hello World"; |
uint8_t DispPtr = 0; |
/************************************/ |
/* Clear LCD Buffer */ |
/************************************/ |
void LCD_Clear(void) |
{ |
uint8_t i; |
for( i = 0; i < DISPLAYBUFFSIZE; i++) DisplayBuff[i] = ' '; |
} |
/************************************/ |
/* Update Menu on LCD */ |
/************************************/ |
// Display with 20 characters in 4 lines |
void LCD_PrintMenu(void) |
{ |
int16_t i1,i2,i3; |
uint8_t sign; |
if(RemoteKeys & KEY1) |
{ |
if(MenuItem) MenuItem--; |
else MenuItem = MaxMenuItem; |
} |
if(RemoteKeys & KEY2) |
{ |
if(MenuItem == MaxMenuItem) MenuItem = 0; |
else MenuItem++; |
} |
if((RemoteKeys & KEY1) && (RemoteKeys & KEY2)) MenuItem = 0; |
LCD_Clear(); |
if(MenuItem > MaxMenuItem) MenuItem = MaxMenuItem; |
// print menu item number in the upper right corner |
if(MenuItem < 10) |
{ |
LCD_printfxy(17,0,"[%i]",MenuItem); |
} |
else |
{ |
LCD_printfxy(16,0,"[%i]",MenuItem); |
} |
switch(MenuItem) |
{ |
case 0:// Version Info Menu Item |
LCD_printfxy(0,0,"+ Follow Me +"); |
#ifdef USE_SDLOGGER |
LCD_printfxy(0,1,"HW: SD-Logger"); |
#endif |
#ifdef USE_FOLLOWME |
LCD_printfxy(0,1,"HW: Follow-Me"); |
#endif |
LCD_printfxy(0,2,"SW: %d.%d%c", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH+'a'); |
LCD_printfxy(0,3," "); |
break; |
case 1: |
if (GPSData.Status == INVALID) |
{ |
LCD_printfxy(0,0,"No GPS data"); |
LCD_printfxy(0,1,"Lon: "); |
LCD_printfxy(0,2,"Lat: "); |
LCD_printfxy(0,3,"Alt: "); |
} |
else // newdata or processed |
{ |
switch (GPSData.SatFix) |
{ |
case SATFIX_NONE: |
LCD_printfxy(0,0,"Sats:%02d Fix:None", GPSData.NumOfSats); |
break; |
case SATFIX_2D: |
LCD_printfxy(0,0,"Sats:%02d Fix:2D ", GPSData.NumOfSats); |
break; |
case SATFIX_3D: |
LCD_printfxy(0,0,"Sats:%02d Fix:3D ", GPSData.NumOfSats); |
break; |
default: |
LCD_printfxy(0,0,"Sats:%02d Fix:?? ", GPSData.NumOfSats); |
break; |
} |
if(GPSData.Position.Longitude < 0) sign = '-'; |
else sign = '+'; |
i1 = (int16_t)(GPSData.Position.Longitude/10000000L); |
i2 = abs((int16_t)((GPSData.Position.Longitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Longitude%10000000L)%10000L)/10L)); |
LCD_printfxy(0,1,"Lon: %c%d.%.3d%.3d deg",sign, i1, i2, i3); |
if(GPSData.Position.Latitude < 0) sign = '-'; |
else sign = '+'; |
i1 = (int16_t)(GPSData.Position.Latitude/10000000L); |
i2 = abs((int16_t)((GPSData.Position.Latitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Latitude%10000000L)%10000L)/10L)); |
LCD_printfxy(0,2,"Lat: %c%d.%.3d%.3d deg",sign, i1, i2, i3); |
if(GPSData.Position.Altitude < 0) sign = '-'; |
else sign = '+'; |
i1 = (int16_t)(GPSData.Position.Altitude/1000L); |
i2 = abs((int16_t)(GPSData.Position.Altitude%1000L)); |
LCD_printfxy(0,3,"Alt: %c%04d.%.03d m",sign, i1, i2); |
} |
break; |
case 2: |
if (GPSData.Status == INVALID) |
{ |
LCD_printfxy(0,0,"No GPS data"); |
LCD_printfxy(0,1,"Speed N: "); |
LCD_printfxy(0,2,"Speed E: "); |
LCD_printfxy(0,3,"Speed T: "); |
} |
else // newdata or processed |
{ |
switch (GPSData.SatFix) |
{ |
case SATFIX_NONE: |
LCD_printfxy(0,0,"Sats:%02d Fix:None", GPSData.NumOfSats); |
break; |
case SATFIX_2D: |
LCD_printfxy(0,0,"Sats:%02d Fix:2D ", GPSData.NumOfSats); |
break; |
case SATFIX_3D: |
LCD_printfxy(0,0,"Sats:%02d Fix:3D ", GPSData.NumOfSats); |
break; |
default: |
LCD_printfxy(0,0,"Sats:%02d Fix:?? ", GPSData.NumOfSats); |
break; |
} |
LCD_printfxy(0,1,"Speed N: %+4d cm/s",(int16_t)GPSData.Speed_North); |
LCD_printfxy(0,2,"Speed E: %+4d cm/s",(int16_t)GPSData.Speed_East); |
LCD_printfxy(0,3,"Speed T: %+4d cm/s",(int16_t)GPSData.Speed_Top); |
} |
break; |
case 3: |
LCD_printfxy(0,0,"GPS UTC Time"); |
if (!SystemTime.Valid) |
{ |
LCD_printfxy(0,1," "); |
LCD_printfxy(0,2," No time data! "); |
LCD_printfxy(0,3," "); |
} |
else // newdata or processed |
{ |
LCD_printfxy(0,1," "); |
LCD_printfxy(0,2,"Date: %02i/%02i/%04i",SystemTime.Month, SystemTime.Day, SystemTime.Year); |
LCD_printfxy(0,3,"Time: %02i:%02i:%02i.%03i", SystemTime.Hour, SystemTime.Min, SystemTime.Sec, SystemTime.mSec); |
} |
break; |
default: MaxMenuItem = MenuItem - 1; |
MenuItem = 0; |
break; |
} |
RemoteKeys = 0; |
} |
/FollowMe/menu.h |
---|
0,0 → 1,17 |
#ifndef _MENU_H |
#define _MENU_H |
#include <inttypes.h> |
#define DISPLAYBUFFSIZE 80 |
extern void LCD_PrintMenu(void); |
extern void LCD_Clear(void); |
extern int8_t DisplayBuff[DISPLAYBUFFSIZE]; |
extern uint8_t DispPtr; |
extern uint8_t MenuItem; |
extern uint8_t MaxMenuItem; |
extern uint8_t RemoteKeys; |
#endif //_MENU_H |
/FollowMe/old_macros.h |
---|
0,0 → 1,47 |
/* |
For backwards compatibility only. |
Ingo Busker ingo@mikrocontroller.com |
*/ |
#ifndef cbi |
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) |
#endif |
#ifndef sbi |
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) |
#endif |
#ifndef inb |
#define inb(sfr) _SFR_BYTE(sfr) |
#endif |
#ifndef outb |
#define outb(sfr, val) (_SFR_BYTE(sfr) = (val)) |
#endif |
#ifndef inw |
#define inw(sfr) _SFR_WORD(sfr) |
#endif |
#ifndef outw |
#define outw(sfr, val) (_SFR_WORD(sfr) = (val)) |
#endif |
#ifndef outp |
#define outp(val, sfr) outb(sfr, val) |
#endif |
#ifndef inp |
#define inp(sfr) inb(sfr) |
#endif |
#ifndef BV |
#define BV(bit) _BV(bit) |
#endif |
#ifndef PRG_RDB |
#define PRG_RDB pgm_read_byte |
#endif |
/FollowMe/printf_P.c |
---|
0,0 → 1,483 |
// Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist nicht von der Lizenz für den MikroKopter-Teil unterstellt |
/* |
Copyright (C) 1993 Free Software Foundation |
This file is part of the GNU IO Library. This library is free |
software; you can redistribute it and/or modify it under the |
terms of the GNU General Public License as published by the |
Free Software Foundation; either version 2, or (at your option) |
any later version. |
This library is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
You should have received a copy of the GNU General Public License |
along with this library; see the file COPYING. If not, write to the Free |
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
As a special exception, if you link this library with files |
compiled with a GNU compiler to produce an executable, this does not cause |
the resulting executable to be covered by the GNU General Public License. |
This exception does not however invalidate any other reasons why |
the executable file might be covered by the GNU General Public License. */ |
/* |
* Copyright (c) 1990 Regents of the University of California. |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* 3. [rescinded 22 July 1999] |
* 4. Neither the name of the University nor the names of its contributors |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
*/ |
/****************************************************************************** |
This file is a patched version of printf called _printf_P |
It is made to work with avr-gcc for Atmel AVR MCUs. |
There are some differences from standard printf: |
1. There is no floating point support (with fp the code is about 8K!) |
2. Return type is void |
3. Format string must be in program memory (by using macro printf this is |
done automaticaly) |
4. %n is not implemented (just remove the comment around it if you need it) |
5. If LIGHTPRINTF is defined, the code is about 550 bytes smaller and the |
folowing specifiers are disabled : |
space # * . - + p s o O |
6. A function void uart_sendchar(char c) is used for output. The UART must |
be initialized before using printf. |
Alexander Popov |
sasho@vip.orbitel.bg |
******************************************************************************/ |
/* |
* Actual printf innards. |
* |
* This code is large and complicated... |
*/ |
#include <string.h> |
#ifdef __STDC__ |
#include <stdarg.h> |
#else |
#include <varargs.h> |
#endif |
#include "old_macros.h" |
#include "printf_P.h" |
#include "menu.h" |
#include "uart0.h" |
//#define LIGHTPRINTF |
char PrintZiel; |
char Putchar(char zeichen) |
{ |
if(PrintZiel == OUT_LCD) { DisplayBuff[DispPtr++] = zeichen; return(1);} |
else return(uart_putchar(zeichen)); |
} |
void PRINT(const char * ptr, unsigned int len) |
{ |
for(;len;len--) Putchar(*ptr++); |
} |
void PRINTP(const char * ptr, unsigned int len) |
{ |
for(;len;len--) Putchar(pgm_read_byte(ptr++)); |
} |
void PAD_SP(signed char howmany) |
{ |
for(;howmany>0;howmany--) Putchar(' '); |
} |
void PAD_0(signed char howmany) |
{ |
for(;howmany>0;howmany--) Putchar('0'); |
} |
#define BUF 40 |
/* |
* Macros for converting digits to letters and vice versa |
*/ |
#define to_digit(c) ((c) - '0') |
#define is_digit(c) ((c)<='9' && (c)>='0') |
#define to_char(n) ((n) + '0') |
/* |
* Flags used during conversion. |
*/ |
#define LONGINT 0x01 /* long integer */ |
#define LONGDBL 0x02 /* long double; unimplemented */ |
#define SHORTINT 0x04 /* short integer */ |
#define ALT 0x08 /* alternate form */ |
#define LADJUST 0x10 /* left adjustment */ |
#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ |
#define HEXPREFIX 0x40 /* add 0x or 0X prefix */ |
void _printf_P (char ziel,char const *fmt0, ...) /* Works with string from FLASH */ |
{ |
va_list ap; |
register const char *fmt; /* format string */ |
register char ch; /* character from fmt */ |
register int n; /* handy integer (short term usage) */ |
register char *cp; /* handy char pointer (short term usage) */ |
const char *fmark; /* for remembering a place in fmt */ |
register unsigned char flags; /* flags as above */ |
signed char width; /* width from format (%8d), or 0 */ |
signed char prec; /* precision from format (%.3d), or -1 */ |
char sign; /* sign prefix (' ', '+', '-', or \0) */ |
unsigned long _ulong=0; /* integer arguments %[diouxX] */ |
#define OCT 8 |
#define DEC 10 |
#define HEX 16 |
unsigned char base; /* base for [diouxX] conversion */ |
signed char dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
signed char dpad; /* extra 0 padding needed for integers */ |
signed char fieldsz; /* field size expanded by sign, dpad etc */ |
/* The initialization of 'size' is to suppress a warning that |
'size' might be used unitialized. It seems gcc can't |
quite grok this spaghetti code ... */ |
signed char size = 0; /* size of converted field or string */ |
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
char ox[2]; /* space for 0x hex-prefix */ |
PrintZiel = ziel; // bestimmt, LCD oder UART |
va_start(ap, fmt0); |
fmt = fmt0; |
/* |
* Scan the format for conversions (`%' character). |
*/ |
for (;;) { |
for (fmark = fmt; (ch = pgm_read_byte(fmt)) != '\0' && ch != '%'; fmt++) |
/* void */; |
if ((n = fmt - fmark) != 0) { |
PRINTP(fmark, n); |
} |
if (ch == '\0') |
goto done; |
fmt++; /* skip over '%' */ |
flags = 0; |
dprec = 0; |
width = 0; |
prec = -1; |
sign = '\0'; |
rflag: ch = PRG_RDB(fmt++); |
reswitch: |
#ifdef LIGHTPRINTF |
if (ch=='o' || ch=='u' || (ch|0x20)=='x') { |
#else |
if (ch=='u' || (ch|0x20)=='x') { |
#endif |
if (flags&LONGINT) { |
_ulong=va_arg(ap, unsigned long); |
} else { |
register unsigned int _d; |
_d=va_arg(ap, unsigned int); |
_ulong = flags&SHORTINT ? (unsigned long)(unsigned short)_d : (unsigned long)_d; |
} |
} |
#ifndef LIGHTPRINTF |
if(ch==' ') { |
/* |
* ``If the space and + flags both appear, the space |
* flag will be ignored.'' |
* -- ANSI X3J11 |
*/ |
if (!sign) |
sign = ' '; |
goto rflag; |
} else if (ch=='#') { |
flags |= ALT; |
goto rflag; |
} else if (ch=='*'||ch=='-') { |
if (ch=='*') { |
/* |
* ``A negative field width argument is taken as a |
* - flag followed by a positive field width.'' |
* -- ANSI X3J11 |
* They don't exclude field widths read from args. |
*/ |
if ((width = va_arg(ap, int)) >= 0) |
goto rflag; |
width = -width; |
} |
flags |= LADJUST; |
flags &= ~ZEROPAD; /* '-' disables '0' */ |
goto rflag; |
} else if (ch=='+') { |
sign = '+'; |
goto rflag; |
} else if (ch=='.') { |
if ((ch = PRG_RDB(fmt++)) == '*') { |
n = va_arg(ap, int); |
prec = n < 0 ? -1 : n; |
goto rflag; |
} |
n = 0; |
while (is_digit(ch)) { |
n = n*10 + to_digit(ch); |
ch = PRG_RDB(fmt++); |
} |
prec = n < 0 ? -1 : n; |
goto reswitch; |
} else |
#endif /* LIGHTPRINTF */ |
if (ch=='0') { |
/* |
* ``Note that 0 is taken as a flag, not as the |
* beginning of a field width.'' |
* -- ANSI X3J11 |
*/ |
if (!(flags & LADJUST)) |
flags |= ZEROPAD; /* '-' disables '0' */ |
goto rflag; |
} else if (ch>='1' && ch<='9') { |
n = 0; |
do { |
n = 10 * n + to_digit(ch); |
ch = PRG_RDB(fmt++); |
} while (is_digit(ch)); |
width = n; |
goto reswitch; |
} else if (ch=='h') { |
flags |= SHORTINT; |
goto rflag; |
} else if (ch=='l') { |
flags |= LONGINT; |
goto rflag; |
} else if (ch=='c') { |
*(cp = buf) = va_arg(ap, int); |
size = 1; |
sign = '\0'; |
} else if (ch=='D'||ch=='d'||ch=='i') { |
if(ch=='D') |
flags |= LONGINT; |
if (flags&LONGINT) { |
_ulong=va_arg(ap, long); |
} else { |
register int _d; |
_d=va_arg(ap, int); |
_ulong = flags&SHORTINT ? (long)(short)_d : (long)_d; |
} |
if ((long)_ulong < 0) { |
_ulong = -_ulong; |
sign = '-'; |
} |
base = DEC; |
goto number; |
} else |
/* |
if (ch=='n') { |
if (flags & LONGINT) |
*va_arg(ap, long *) = ret; |
else if (flags & SHORTINT) |
*va_arg(ap, short *) = ret; |
else |
*va_arg(ap, int *) = ret; |
continue; // no output |
} else |
*/ |
#ifndef LIGHTPRINTF |
if (ch=='O'||ch=='o') { |
if (ch=='O') |
flags |= LONGINT; |
base = OCT; |
goto nosign; |
} else if (ch=='p') { |
/* |
* ``The argument shall be a pointer to void. The |
* value of the pointer is converted to a sequence |
* of printable characters, in an implementation- |
* defined manner.'' |
* -- ANSI X3J11 |
*/ |
/* NOSTRICT */ |
_ulong = (unsigned int)va_arg(ap, void *); |
base = HEX; |
flags |= HEXPREFIX; |
ch = 'x'; |
goto nosign; |
} else if (ch=='s') { // print a string from RAM |
if ((cp = va_arg(ap, char *)) == NULL) { |
cp=buf; |
cp[0] = '('; |
cp[1] = 'n'; |
cp[2] = 'u'; |
cp[4] = cp[3] = 'l'; |
cp[5] = ')'; |
cp[6] = '\0'; |
} |
if (prec >= 0) { |
/* |
* can't use strlen; can only look for the |
* NUL in the first `prec' characters, and |
* strlen() will go further. |
*/ |
char *p = (char*)memchr(cp, 0, prec); |
if (p != NULL) { |
size = p - cp; |
if (size > prec) |
size = prec; |
} else |
size = prec; |
} else |
size = strlen(cp); |
sign = '\0'; |
} else |
#endif /* LIGHTPRINTF */ |
if(ch=='U'||ch=='u') { |
if (ch=='U') |
flags |= LONGINT; |
base = DEC; |
goto nosign; |
} else if (ch=='X'||ch=='x') { |
base = HEX; |
/* leading 0x/X only if non-zero */ |
if (flags & ALT && _ulong != 0) |
flags |= HEXPREFIX; |
/* unsigned conversions */ |
nosign: sign = '\0'; |
/* |
* ``... diouXx conversions ... if a precision is |
* specified, the 0 flag will be ignored.'' |
* -- ANSI X3J11 |
*/ |
number: if ((dprec = prec) >= 0) |
flags &= ~ZEROPAD; |
/* |
* ``The result of converting a zero value with an |
* explicit precision of zero is no characters.'' |
* -- ANSI X3J11 |
*/ |
cp = buf + BUF; |
if (_ulong != 0 || prec != 0) { |
register unsigned char _d,notlastdigit; |
do { |
notlastdigit=(_ulong>=base); |
_d = _ulong % base; |
if (_d<10) { |
_d+='0'; |
} else { |
_d+='a'-10; |
if (ch=='X') _d&=~0x20; |
} |
*--cp=_d; |
_ulong /= base; |
} while (notlastdigit); |
#ifndef LIGHTPRINTF |
// handle octal leading 0 |
if (base==OCT && flags & ALT && *cp != '0') |
*--cp = '0'; |
#endif |
} |
size = buf + BUF - cp; |
} else { //default |
/* "%?" prints ?, unless ? is NUL */ |
if (ch == '\0') |
goto done; |
/* pretend it was %c with argument ch */ |
cp = buf; |
*cp = ch; |
size = 1; |
sign = '\0'; |
} |
/* |
* All reasonable formats wind up here. At this point, |
* `cp' points to a string which (if not flags&LADJUST) |
* should be padded out to `width' places. If |
* flags&ZEROPAD, it should first be prefixed by any |
* sign or other prefix; otherwise, it should be blank |
* padded before the prefix is emitted. After any |
* left-hand padding and prefixing, emit zeroes |
* required by a decimal [diouxX] precision, then print |
* the string proper, then emit zeroes required by any |
* leftover floating precision; finally, if LADJUST, |
* pad with blanks. |
*/ |
/* |
* compute actual size, so we know how much to pad. |
*/ |
fieldsz = size; |
dpad = dprec - size; |
if (dpad < 0) |
dpad = 0; |
if (sign) |
fieldsz++; |
else if (flags & HEXPREFIX) |
fieldsz += 2; |
fieldsz += dpad; |
/* right-adjusting blank padding */ |
if ((flags & (LADJUST|ZEROPAD)) == 0) |
PAD_SP(width - fieldsz); |
/* prefix */ |
if (sign) { |
PRINT(&sign, 1); |
} else if (flags & HEXPREFIX) { |
ox[0] = '0'; |
ox[1] = ch; |
PRINT(ox, 2); |
} |
/* right-adjusting zero padding */ |
if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) |
PAD_0(width - fieldsz); |
/* leading zeroes from decimal precision */ |
PAD_0(dpad); |
/* the string or number proper */ |
PRINT(cp, size); |
/* left-adjusting padding (always blank) */ |
if (flags & LADJUST) |
PAD_SP(width - fieldsz); |
} |
done: |
va_end(ap); |
} |
/FollowMe/printf_P.h |
---|
0,0 → 1,19 |
#ifndef _PRINTF_P_H_ |
#define _PRINTF_P_H_ |
#include <avr/pgmspace.h> |
#define OUT_V24 0 |
#define OUT_LCD 1 |
void _printf_P (char, char const *fmt0, ...); |
extern char PrintZiel; |
#define printf_P(format, args...) _printf_P(OUT_V24,format , ## args) |
#define printf(format, args...) _printf_P(OUT_V24,PSTR(format) , ## args) |
#define LCD_printfxy(x,y,format, args...) { DispPtr = y * 20 + x; _printf_P(OUT_LCD,PSTR(format) , ## args);} |
#define LCD_printf(format, args...) { _printf_P(OUT_LCD,PSTR(format) , ## args);} |
#endif |
/FollowMe/sdc.c |
---|
0,0 → 1,688 |
#include <avr/io.h> |
#include <util/delay.h> |
#include <string.h> |
#include "sdc.h" |
#include "ssc.h" |
#include "timer0.h" |
#include "printf_P.h" |
#include "crc16.h" |
//#define _SD_DEBUG |
#define CMD_GO_IDLE_STATE 0x00 /* CMD00: response R1 */ |
#define CMD_SEND_OP_COND 0x01 /* CMD01: response R1 */ |
#define CMD_SEND_IF_COND 0x08 /* CMD08: response R7 */ |
#define CMD_SEND_CSD 0x09 /* CMD09: response R1 */ |
#define CMD_SEND_CID 0x0A /* CMD10: response R1 */ |
#define CMD_SEND_STATUS 0x0D /* CMD13: response R2 */ |
#define CMD_SET_BLOCKLEN 0x10 /* CMD16: arg0[31:0]: block length, response R1*/ |
#define CMD_READ_SINGLE_BLOCK 0x11 /* CMD17: arg0[31:0]: data address, response R1 */ |
#define CMD_WRITE_SINGLE_BLOCK 0x18 /* CMD24: arg0[31:0]: data address, response R1 */ |
#define CMD_APP_CMD 0x37 /* CMD55: response R1 */ |
#define CMD_READ_OCR 0x3A /* CMD58: response R3 */ |
#define CMD_CRC_ON_OFF 0x3B /* CMD59: arg0[31:1]: stuff bits, arg0[0:0]: crc option, response R1 */ |
#define ACMD_SEND_OP_COND 0x29 /* ACMD41: arg0[31]: stuff bits, arg0[30]: HCS, arg0[29:0] stuff bits*, response R1 */ |
#define R1_NO_ERR 0x00 |
#define R1_IDLE_STATE 0x01 |
#define R1_ERASE_RESET 0x02 |
#define R1_ILLEGAL_CMD 0x04 |
#define R1_COM_CRC_ERR 0x08 |
#define R1_ERASE_SEQUENCE_ERR 0x10 |
#define R1_ADDRESS_ERR 0x20 |
#define R1_PARAMETER_ERR 0x40 |
#define R1_BAD_RESPONSE 0x80 |
#define R2_NO_ERR 0x00 |
#define R2_CARD_LOCKED 0x01 |
#define R2_ERASE_WRITE_PROT_ERR 0x02 |
#define R2_UNKOWN_ERR 0x04 |
#define R2_CARD_CTRL_ERR 0x08 |
#define R2_CARD_ECC_ERR 0x10 |
#define R2_WRITE_PROT_ERR 0x20 |
#define R2_ERASE_PARAM_ERR 0x40 |
#define R2_OUT_OF_RANGE_ERR 0x80 |
#define DATA_START_TOKEN 0xFE |
#define DATA_RESPONSE_MASK 0x1F |
#define DATA_RESPONSE_OK 0x05 |
#define DATA_RESPONSE_CRC_ERR 0x0B |
#define DATA_RESPONSE_WRITE_ERR 0x1D |
typedef enum |
{ |
VER_UNKNOWN, |
VER_1X, |
VER_20 |
} SDVersion_t; |
typedef struct |
{ |
uint8_t Valid; |
SDVersion_t Version; // HW-Version |
uint32_t Capacity; // Memory capacity in bytes |
uint8_t CID[16]; // CID register |
uint8_t CSD[16]; // CSD register |
} __attribute__((packed)) SDCardInfo_t; |
volatile SDCardInfo_t SDCardInfo; |
//________________________________________________________________________________________________________________________________________ |
// Function: CRC7(uint8_t* cmd, uint32_t len); |
// |
// Description: This function calculated the CRC7 checksum used in the last byte of a spi command frame. |
// |
// |
// Returnvalue: the function returns the crc7 including bit 0 set to 1 |
//________________________________________________________________________________________________________________________________________ |
uint8_t CRC7(uint8_t *cmd, uint32_t len) |
{ |
uint8_t i, a; |
uint8_t crc, Data; |
crc = 0; // init CRC buffer |
for (a = 0; a < len ;a++) // for every byte in the msg |
{ |
Data = cmd[a]; |
for (i=0;i<8;i++) // for every bit in the byte |
{ |
crc <<= 1; // shift crc |
if ((Data & 0x80)^(crc & 0x80)) crc ^=0x09; //xor |
Data <<= 1; // shift data for next bit |
} |
} |
crc = (crc<<1)|1; // set terminating bit to 1 |
return(crc); |
} |
uint8_t SDC_WaitForBusy(uint16_t timeout) |
{ |
uint8_t rsp = 0; |
uint16_t timestamp = 0; |
SSC_Enable(); // enable chipselect. |
timestamp = SetDelay(timeout); |
do |
{ |
rsp = SSC_GetChar(); |
if(CheckDelay(timestamp)) break; |
}while(rsp != 0xFF); // wait while card is busy (data out low) |
return(rsp); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_SendCMDR1(uint8_t CmdNo, uint32_t arg); |
// |
// Description: This function send a command frame to the SD-Card in spi-mode. |
// |
// |
// Returnvalue: The function returns the first response byte like for R1 commands |
//________________________________________________________________________________________________________________________________________ |
uint8_t SDC_SendCMDR1(uint8_t CmdNo, uint32_t arg) |
{ |
uint8_t r1; |
uint16_t timeout = 0; |
uint16_t a; |
uint8_t cmd[6]; |
cmd[0] = 0x40|CmdNo; // set command index |
cmd[1] = (arg & 0xFF000000)>>24; |
cmd[2] = (arg & 0x00FF0000)>>16; |
cmd[3] = (arg & 0x0000FF00)>>8; |
cmd[4] = (arg & 0x000000FF); |
cmd[5] = CRC7(cmd, 5); // update checksum |
#ifdef _SD_DEBUG |
printf("\r\nCmd=%02X, arg=%04X%04X", CmdNo, (uint16_t)(arg>>16), (uint16_t)(0xFFFF & arg)); |
#endif |
SSC_Disable(); // disable chipselect. |
SSC_PutChar(0xFF); // dummy to sync |
SSC_Enable(); // enable chipselect. |
SDC_WaitForBusy(500); // wait 500ms until card is busy |
for (a = 0;a < 6; a++) // send the command sequence to the sdcard (6 bytes) |
{ |
SSC_PutChar(cmd[a]); |
_delay_loop_2(10); |
} |
// get response byte |
do |
{ |
r1 = SSC_GetChar(); // get byte from sd-card |
if (timeout++ > 500) break; |
}while(r1 == 0xFF); // wait for the response byte from sd-card. |
#ifdef _SD_DEBUG |
printf("-->R1=%02X", r1); |
#endif |
return(r1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_SendACMDR1(uint8_t CmdNo, uint32_t arg); |
// |
// Description: This function send a application command frame to the SD-Card in spi-mode. |
// |
// |
// Returnvalue: The function returns the first response byte like for R1 commands |
//________________________________________________________________________________________________________________________________________ |
uint8_t SDC_SendACMDR1(uint8_t CmdNo, uint32_t arg) |
{ |
uint8_t r1 = 0xFF; |
r1 = SDC_SendCMDR1(CMD_APP_CMD, 0UL); |
if(r1 & R1_BAD_RESPONSE) return(r1); |
r1 = SDC_SendCMDR1(CmdNo, arg); |
return(r1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetData(uint8_t * cmd ,u8 *Buffer, u32 len); |
// |
// Description: This function sneds cmd an reads a datablock of len from the sd-card |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetData(uint8_t CmdNo, uint32_t addr, uint8_t *Buffer, uint32_t len) |
{ |
uint8_t rsp; |
uint16_t a, crc16; |
SD_Result_t result = SD_ERROR_UNKNOWN; |
// send the command |
rsp = SDC_SendCMDR1(CmdNo, addr); |
if (rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
do |
{ |
rsp = SSC_GetChar(); |
if((rsp & 0xF0) == 0x00) // data error token |
{ |
result = SD_ERROR_READ_DATA; |
goto end; |
} |
}while(rsp != DATA_START_TOKEN); |
// data start token received |
for (a = 0; a < len; a++) // read the block from the SSC |
{ |
Buffer[a] = SSC_GetChar(); |
} |
// Read two bytes CRC16-Data checksum |
crc16 = SSC_GetChar(); // highbyte first |
crc16 = (crc16<<8)|SSC_GetChar(); // lowbyte last |
/* if(crc16 != CRC16(Buffer, len)) result = SD_ERROR_CRC_DATA; |
else */result = SD_SUCCESS; |
end: |
if(result != SD_SUCCESS) |
{ |
printf("Error %02X reading data from sd card (R1=%02X).\r\n", result, rsp); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_PrintCID(u8 * pCID); |
// |
// Description: This function prints the CIS register in a human readable format. |
// |
// |
// Returnvalue: the function returns nothing |
//________________________________________________________________________________________________________________________________________ |
void SDC_PrintCID(uint8_t * pCID) |
{ |
uint8_t pn[6]; |
uint16_t temp1, temp2; |
printf("\r\n Manufacturer ID: %i\r\n", pCID[0]); |
memcpy(pn, &pCID[1], 2); |
pn[2] = '\0'; // terminate string |
printf(" Application ID: %s\r\n",pn); |
memcpy(pn, &pCID[3], 5); |
pn[5] = '\0'; // terminate string |
printf(" Product Name: %s\r\n",pn); |
printf(" Product Rev.: %i.%i\r\n",pCID[8]>>4, pCID[8]&0xF); |
printf(" Serial No.: "); |
for(temp1 = 0; temp1<4; temp1++) |
{ |
printf("%02X", pCID[9+temp1]); |
} |
printf("\r\n"); |
temp1 = pCID[14] & 0x0F; // month |
temp2 = ((pCID[14]>>4)|(pCID[13]<<4)) + 2000; // year |
printf(" Manufac. Date: %i/%i\r\n\r\n",temp1, temp2); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetCID(uint8_t * pCID); |
// |
// Description: This function reads the CIS register form the sd card in spi mode. |
// |
// |
// Returnvalue: the function returns error state |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetCID(uint8_t * pCID) |
{ |
return SDC_GetData(CMD_SEND_CID, 0UL, pCID, 16); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetCSD(uint8_t * pCSD); |
// |
// Description: This function reads the CSD register form the sd card in spi mode. |
// |
// |
// Returnvalue: the function returns error state |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetCSD(uint8_t * pCSD) |
{ |
return SDC_GetData(CMD_SEND_CSD, 0UL, pCSD, 16); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_Init(void); |
// |
// Description: This function initialises the SDCard to spi-mode. |
// |
// |
// Returnvalue: the function returns 0 if the initialisation was successfull otherwise the function returns an errorcode. |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_Init(void) |
{ |
uint16_t timeout = 0; |
uint8_t rsp[6]; // SD-SPI response buffer |
SD_Result_t result = SD_ERROR_UNKNOWN; |
if(SD_SWITCH) // init only if the SD-Switch is indicating a card in the slot |
{ |
printf("\r\n SSC init..."); |
SSC_Init(); |
printf("ok"); |
//_delay_loop_2(1050); |
printf("\r\n SDC init..."); |
SDCardInfo.Valid = 0; |
/* The host shall supply power to the card so that the voltage is reached to Vdd_min within 250ms and |
start to supply at least 74 SD clocks to the SD card with keeping cmd line to high. In case of SPI |
mode, CS shall be held to high during 74 clock cycles. */ |
SSC_Disable(); // set SD_CS high |
for (timeout = 0; timeout < 15; timeout++) // 15*8 = 120 cycles |
{ |
SSC_PutChar(0xFF); |
} |
// switch to idle state |
#ifdef _SD_DEBUG |
printf("\r\nGoing idle state.."); |
#endif |
timeout = 0; |
do |
{ |
rsp[0] = SDC_SendCMDR1(CMD_GO_IDLE_STATE, 0UL); |
if (timeout++ > 500) |
{ |
printf("reset timeout"); |
result = SD_ERROR_RESET; |
goto end; |
} |
}while(rsp[0] != R1_IDLE_STATE); |
// enable crc feature |
/* if(SDC_SendCMDR1(CMD_CRC_ON_OFF, 1UL) != R1_IDLE_STATE) |
{ |
printf("Bad cmd59 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
}*/ |
// check for card hw version |
// 2.7-3.6V Range = 0x01, check pattern 0xAA |
rsp[0] = SDC_SendCMDR1(CMD_SEND_IF_COND, 0x000001AA); |
// answer to cmd58 is an R7 response (R1+ 4Byte IFCond) |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad cmd8 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(rsp[0] & R1_ILLEGAL_CMD) |
{ |
//Ver1.X SD Memory Card or not a SD Memory Card |
SDCardInfo.Version = VER_1X; |
} |
else |
{ |
// Ver2.00 or later SD Memory Card |
// reading the remaining bytes of the R7 response |
SDCardInfo.Version = VER_20; |
for(timeout = 1; timeout < 5; timeout++) |
{ |
rsp[timeout] = SSC_GetChar(); |
} |
//check pattern |
if(rsp[4]!= 0xAA) |
{ |
printf("Bad cmd8 R7 check pattern.\r\n"); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if ( (rsp[3] & 0x0F)!= 0x01 ) // voltage range is not 2.7-3.6V |
{ |
printf("Card is incompatible to 3.3V.\r\n"); |
result = SD_ERROR_BAD_VOLTAGE_RANGE; |
goto end; |
} |
} |
rsp[0] = SDC_SendCMDR1(CMD_READ_OCR, 0UL); |
// answer to cmd58 is an R3 response (R1 + 4Byte OCR) |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad cmd58 R1 %02x.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(rsp[0] & R1_ILLEGAL_CMD) |
{ |
printf("Not an SD-CARD."); |
result = SD_ERROR_NO_SDCARD; |
goto end; |
} |
// read 4 bytes of OCR register |
for(timeout = 1; timeout < 5; timeout++) |
{ |
rsp[timeout] = SSC_GetChar(); |
} |
// FollowMe & SD-Logger uses 3.3 V, therefore check for bit 20 & 21 |
if((rsp[2] & 0x30) != 0x30) |
{ |
// supply voltage is not supported by sd-card |
printf("Card is incompatible to 3.3V."); |
result = SD_ERROR_BAD_VOLTAGE_RANGE; |
goto end; |
} |
// Initialize the sd-card sending continously ACMD_SEND_OP_COND (only supported by SD cards) |
timeout = SetDelay(2000); // set timeout to 2000 ms (large cards tend to longer) |
do |
{ |
rsp[0] = SDC_SendACMDR1(ACMD_SEND_OP_COND, 0UL); |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad Acmd41 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(CheckDelay(timeout)) |
{ |
printf("Init timeout."); |
result = SD_ERROR_INITIALIZE; |
goto end; |
} |
} while(rsp[0] & R1_IDLE_STATE); // loop until idle state |
if(rsp[0] != R1_NO_ERR) |
{ |
printf("Init error."); |
result = SD_ERROR_INITIALIZE; |
goto end; |
} |
/* set block size to 512 bytes */ |
if(SDC_SendCMDR1(CMD_SET_BLOCKLEN, 512UL) != R1_NO_ERR) |
{ |
printf("Error setting block length to 512."); |
result = SD_ERROR_SET_BLOCKLEN; |
goto end; |
} |
//SSC_Disable(); // set SD_CS high |
// here is the right place to inrease the SPI baud rate to maximum |
//SSC_Enable(); // set SD_CS high |
// read CID register |
result = SDC_GetCID((uint8_t *)&SDCardInfo.CID); |
if(result != SD_SUCCESS) |
{ |
printf("Error reading CID.\r\n"); |
goto end; |
} |
// read CSD register |
result = SDC_GetCSD((uint8_t *)&SDCardInfo.CSD); |
if(result != SD_SUCCESS) |
{ |
printf("Error reading CSD."); |
goto end; |
} |
printf("ok\r\n"); |
uint8_t c_size_mult, read_bl_len; |
uint32_t c_size; |
switch(SDCardInfo.CSD[0]>>6) // check CSD Version |
{ |
case 0x00: // if CSD is V1.0 structure (2GB limit) |
/* |
memory capacity = BLOCKNR * BLOCK_LEN |
BLOCKNR = (C_SIZE+1) * MULT |
MULT = 2^(C_SIZE_MULT+2) |
BLOCK_LEN = 2^READ_BL_LEN |
C_SIZE is 12 bits [73:62] in CSD register |
C_SIZE_MULT is 3 bits [49:47] in CSD register |
READ_BL_LEN is 4 bits [83:80] in CSD register |
*/ |
read_bl_len = (SDCardInfo.CSD[5] & 0x0F); //CSD[05] -> [87:80] |
c_size = ((uint32_t)(SDCardInfo.CSD[6] & 0x03))<<10; //CSD[06] -> [79:72] |
c_size |= ((uint32_t)SDCardInfo.CSD[7])<<2; //CSD[07] -> [71:64] |
c_size |= (uint32_t)(SDCardInfo.CSD[8]>>6); //CSD[08] -> [63:56] |
c_size_mult = (SDCardInfo.CSD[9] & 0x03)<<1; //CSD[09] -> [55:48] |
c_size_mult |=(SDCardInfo.CSD[10] & 0x80)>>7; //CSD[10] -> [47:40] |
SDCardInfo.Capacity = (uint32_t)(c_size+1)*(1L<<(c_size_mult+2))*(1L<<read_bl_len); |
break; |
case 0x01: // if CSD is V2.0 structure (HC SD-Card > 2GB) |
/* |
memory capacity = (C_SIZE+1) * 512K byte |
C_SIZE is 22 bits [69:48] in CSR register |
*/ |
c_size = ((uint32_t)(SDCardInfo.CSD[7] & 0x3F))<<16; //CSD[07] -> [71:64] |
c_size |= ((uint32_t)SDCardInfo.CSD[8])<<8; //CSD[08] -> [63:56] |
c_size |= (uint32_t)SDCardInfo.CSD[9]; //CSD[09] -> [55:48]; |
SDCardInfo.Capacity = (c_size + 1)* 512L * 1024L; |
break; |
default: //unknown CSD Version |
SDCardInfo.Capacity = 0; |
break; |
} |
switch(SDCardInfo.Version) |
{ |
case VER_1X: |
printf("\r\n SD-CARD V1.x"); |
break; |
case VER_20: |
printf("\r\n SD-CARD V2.0 or later"); |
default: |
break; |
} |
uint16_t mb_size = (uint16_t)(SDCardInfo.Capacity/(1024L*1024L)); |
printf("\r\n Capacity = %i MB", mb_size); |
SDC_PrintCID((uint8_t *)&SDCardInfo.CID); |
SDCardInfo.Valid = 1; |
// jump point for error condition before |
end: |
SSC_Disable(); |
} |
else |
{ |
SSC_Deinit(); |
SDCardInfo.Valid = 0; |
result = SD_ERROR_NOCARD; |
printf("No Card in Slot."); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_Deinit(void); |
// |
// Description: This function deinitialises the SDCard interface. |
// |
// |
// Returnvalue: the function returns 0 if the initialisation was successfull otherwise the function returns an errorcode. |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_Deinit(void) |
{ |
printf("\r\n SDC deinit..."); |
SSC_Deinit(); |
SDCardInfo.Valid = 0; |
SDCardInfo.Capacity = 0; |
SDCardInfo.Version = VER_UNKNOWN; |
printf("ok"); |
return(SD_SUCCESS); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_PutSector(uint32_t addr, const uint8_t *Buffer) |
// |
// Description: This function writes one sector of data to the SSC |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_PutSector(uint32_t addr, const uint8_t *Buffer) |
{ |
uint8_t rsp; |
uint16_t a, crc16; |
uint16_t timeout = 0; |
SD_Result_t result = SD_ERROR_UNKNOWN; |
addr = addr << 9; // convert sectoradress to byteadress |
rsp = SDC_SendCMDR1(CMD_WRITE_SINGLE_BLOCK, addr); |
if (rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
for (a=0;a<20;a++) // at least one byte |
{ |
SSC_GetChar(); |
} |
crc16 = CRC16(Buffer, 512); // calc checksum for data block |
SSC_PutChar(DATA_START_TOKEN); // send data start of header to the SSC |
for (a=0;a<512;a++) // transmit one sector (normaly 512bytes) of data to the sdcard. |
{ |
SSC_PutChar(Buffer[a]); |
} |
// write two bytes of crc16 to the sdcard |
SSC_PutChar((uint8_t)(crc16>>8)); // write high byte first |
SSC_PutChar((uint8_t)(0x00FF&crc16)); // lowbyte last |
do // wait for data response token |
{ |
rsp = SSC_GetChar(); |
if(timeout++ > 500) |
{ |
result = SD_ERROR_TIMEOUT; |
goto end; |
} |
}while((rsp & 0x11) != 0x01 ); |
// analyse data response token |
switch(rsp & DATA_RESPONSE_MASK) |
{ |
case DATA_RESPONSE_OK: |
result = SD_SUCCESS; |
break; |
case DATA_RESPONSE_CRC_ERR: |
result = SD_ERROR_CRC_DATA; |
goto end; |
break; |
case DATA_RESPONSE_WRITE_ERR: |
result = SD_ERROR_WRITE_DATA; |
goto end; |
break; |
default: |
result = SD_ERROR_UNKNOWN; |
goto end; |
break; |
} |
// wait 2 seconds until the sdcard is busy. |
rsp = SDC_WaitForBusy(2000); |
if(rsp != 0xFF) |
{ |
result = SD_ERROR_TIMEOUT; |
goto end; |
} |
// check card status |
rsp = SDC_SendCMDR1(CMD_SEND_STATUS, 0); |
// first byte of R2 response is like R1 response |
if(rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
SSC_GetChar(); // read out 2nd byte |
goto end; |
} |
// 2nd byte of r2 response |
rsp = SSC_GetChar(); |
if(rsp != R2_NO_ERR) |
{ |
result = SD_ERROR_WRITE_DATA; |
SSC_GetChar(); |
goto end; |
} |
end: |
if(result != SD_SUCCESS) |
{ |
printf("Error %02X writing data to sd card (R=%02X).\r\n", result, rsp); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetSector(uint32_t addr,uint8_t *Buffer); |
// |
// Description: This function reads one sector of data from the SSC |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetSector(uint32_t addr,uint8_t *Buffer) |
{ |
addr = addr << 9; // convert sectoradress to byteadress |
return SDC_GetData(CMD_READ_SINGLE_BLOCK, addr, Buffer, 512); |
} |
/FollowMe/sdc.h |
---|
0,0 → 1,30 |
#ifndef _SDC_H_ |
#define _SDC_H_ |
#include <inttypes.h> |
typedef enum |
{ |
SD_SUCCESS = 0, |
SD_ERROR_NOCARD, |
SD_ERROR_RESET, |
SD_ERROR_INITIALIZE, |
SD_ERROR_BAD_RESPONSE, |
SD_ERROR_BAD_VOLTAGE_RANGE, |
SD_ERROR_NO_SDCARD, |
SD_ERROR_TIMEOUT, |
SD_ERROR_CRC_DATA, |
SD_ERROR_WRITE_DATA, |
SD_ERROR_READ_DATA, |
SD_ERROR_SET_BLOCKLEN, |
SD_ERROR_UNKNOWN |
} SD_Result_t; |
extern SD_Result_t SDC_Init(void); |
extern SD_Result_t SDC_GetSector (uint32_t Addr, uint8_t *pBuffer); |
extern SD_Result_t SDC_PutSector (uint32_t Addr, const uint8_t *pBuffer); |
extern SD_Result_t SDC_Deinit(void); |
#endif |
/FollowMe/ssc.c |
---|
0,0 → 1,216 |
#include <avr/io.h> |
#include "ssc.h" |
//-------------------------------------- Hardware specific definitions -------------------------------------- |
#define PORTR_SPI PINB |
#define PORTW_SPI PORTB //Port to which the sd-card is connected (SPI Port) |
#define PORT_MISO PORTB6 //Port Pin that is connected to the DO of the MMC/SD-card |
#define PORT_MOSI PORTB5 //Port Pin that is connected to DI of the MMC/SD-card |
#define PORT_SCK PORTB7 //Port Pin that is connected the CLK of the MMC/SD-card |
#define PORT_SS PORTB4 //Slave Select is not used in SPI Master Mode, but must be defined |
#define PORT_CS PORTB4 //Port Pin that is connected to /CS of the MMC/SD-Karte |
#ifdef USE_SDLOGGER |
#define __SD_INTERFACE_INVERTED // the interface between the controller and the SD-card uses an inverting leveltranslator (transistorinverter) |
#endif // and therefore the signals to or from the memorycard have to be inverted. |
#ifdef USE_FOLLOWME // uses resitors, therefore its not inverted |
//#define __SD_INTERFACE_INVERTED // the interface between the controller and the MMC/SD-card uses an inverting leveltranslator (transistorinverter) |
#endif |
#define DDR_SPI DDRB |
#define DD_MISO DDB6 //Port Pin that is connected to the DO of the MMC/SD-card |
#define DD_MOSI DDB5 //Port Pin that is connected to DI of the MMC/SD-card |
#define DD_SCK DDB7 //Port Pin that is connected the CLK of the MMC/SD-card |
#define DD_SS DDB4 //Slave Select is not used in SPI Master Mode, but must be defined |
#define DD_CS DDB4 //Port Pin that is connected to /CS of the MMC/SD-Karte |
// for compatibility reasons gcc3.x <-> gcc4.x |
#ifndef SPCR |
#define SPCR SPCR0 |
#endif |
#ifndef SPIE |
#define SPIE SPIE0 |
#endif |
#ifndef SPE |
#define SPE SPE0 |
#endif |
#ifndef DORD |
#define DORD DORD0 |
#endif |
#ifndef MSTR |
#define MSTR MSTR0 |
#endif |
#ifndef CPOL |
#define CPOL CPOL0 |
#endif |
#ifndef CPHA |
#define CPHA CPHA0 |
#endif |
#ifndef SPR1 |
#define SPR1 SPR01 |
#endif |
#ifndef SPR0 |
#define SPR0 SPR00 |
#endif |
#ifndef SPDR |
#define SPDR SPDR0 |
#endif |
#ifndef SPSR |
#define SPSR SPSR0 |
#endif |
#ifndef SPIF |
#define SPIF SPIF0 |
#endif |
#ifndef WCOL |
#define WCOL WCOL0 |
#endif |
#ifndef SPI2X |
#define SPI2X SPI2X0 |
#endif |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Init(void); |
// |
// Description: This function initialises the synchronus serial channel to the sdcard. |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Init(void) |
{ |
// Set MOSI,SCK and CS as output |
DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_CS); |
// set MISO as input |
DDR_SPI &= ~(1<<DD_MISO); |
SSC_Disable(); |
// 20MHz / 32 = 625 kHz |
#ifdef __SD_INTERFACE_INVERTED |
SPCR = (1<<SPE)|(1<<MSTR)|(0<<DORD)|(1<<CPOL)|(0<<CPHA)|(1<<SPR1)|(0<<SPR0); // Enable SSC in mastermode, inverted clockpolarity (idle high) |
#else |
SPCR = (1<<SPE)|(1<<MSTR)|(0<<DORD)|(0<<CPOL)|(0<<CPHA)|(1<<SPR1)|(0<<SPR0); // Enable SSC in mastermode, noninverted clockpolarity (idle low) |
#endif |
SPSR |= (1<<SPI2X); |
// set port pin as input pullup for SD-Card switch |
#ifdef USE_FOLLOWME |
PORTB |= (1 << PORTB2); |
DDRB &= ~(1 << DDB2); |
#endif |
#ifdef USE_SDLOGGER |
PORTB |= (1 << PORTB3); |
DDRB &= ~(1 << DDB3); |
#endif |
} |
void SSC_Deinit(void) |
{ |
SSC_Disable(); |
SPCR = 0; |
SPSR = 0; |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_GetChar(void); |
// |
// Description: This function reads one byte from the SSC |
// |
// |
// Returnvalue: the byte received. |
//________________________________________________________________________________________________________________________________________ |
uint8_t SSC_GetChar (void) |
{ |
uint8_t Byte = 0; |
#ifdef __SD_INTERFACE_INVERTED |
SPDR = 0x00; // send dummy byte to initiate the reading |
#else |
SPDR = 0xFF; // send dummy byte to initiate the reading |
#endif |
while(!(SPSR & (1<<SPIF))) |
{ |
// wait until the data has been read. |
} |
Byte = SPDR; |
#ifdef __SD_INTERFACE_INVERTED |
Byte = ~Byte; |
#endif |
return(Byte); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_PutChar(u8 Byte); |
// |
// Description: This function writes one byte to the SSC |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_PutChar (uint8_t Byte) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
SPDR = ~Byte; // send one byte of data to the SSC |
#else |
SPDR = Byte; // send one byte of data to the SSC |
#endif |
while(!(SPSR & (1<<SPIF))) |
{ |
// wait until the data has been sent. |
} |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Disable(void); |
// |
// Description: This function enables chipselect of the sdcard (active low) |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Disable(void) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
PORTW_SPI &= ~(1<<PORT_CS); // disable chipselect of the sdcard (active low). |
#else |
PORTW_SPI |= (1<<PORT_CS); // disable chipselect of the sdcard (active low). |
#endif |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Enable(void); |
// |
// Description: This function disables chipselect of the sdcard (active low) |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Enable(void) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
PORTW_SPI |= (1<<PORT_CS); // enable chipselect of the sdcard (active low). |
#else |
PORTW_SPI &= ~(1<<PORT_CS); // enable chipselect of the sdcard (active low). |
#endif |
} |
/FollowMe/ssc.h |
---|
0,0 → 1,20 |
#ifndef __SSC_H |
#define __SSC_H |
#include <inttypes.h> |
#ifdef USE_FOLLOWME |
#define SD_SWITCH !(PINB & (1<<PINB2)) |
#endif |
#ifdef USE_SDLOGGER |
#define SD_SWITCH !(PINB & (1<<PINB3)) |
#endif |
extern void SSC_Init(void); |
extern uint8_t SSC_GetChar(void); |
extern void SSC_PutChar(uint8_t); |
extern void SSC_Enable(void); |
extern void SSC_Disable(void); |
extern void SSC_Deinit(void); |
#endif //__SSC_H |
/FollowMe/timer0.c |
---|
0,0 → 1,138 |
#include <inttypes.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "timer0.h" |
volatile uint16_t CountMilliseconds = 0; |
DateTime_t SystemTime; |
volatile uint16_t BeepTime = 0; |
volatile uint16_t BeepModulation = 0xFFFF; |
/*****************************************************/ |
/* Initialize Timer 0 */ |
/*****************************************************/ |
// timer 0 is used for the PWM generation to control the offset voltage at the air pressure sensor |
// Its overflow interrupt routine is used to generate the beep signal and the flight control motor update rate |
void TIMER0_Init(void) |
{ |
uint8_t sreg = SREG; |
// disable all interrupts before reconfiguration |
cli(); |
// configure speaker port as output |
#ifdef USE_FOLLOWME |
// Speaker at PC7 |
DDRC |= (1<<DDC7); |
PORTC &= ~(1<<PORTC7); |
#endif |
// Timer/Counter 0 Control Register A |
// Waveform Generation None (Bits WGM02 = 0, WGM01 = 0, WGM00 = 0) |
TCCR0A &= ~((1<<COM0A0)|(1<<COM0B0)|(1<<COM0A1)|(1<<COM0B1)|(1<<WGM01)|(1<<WGM00)); |
// Timer/Counter 0 Control Register B |
// set clock devider for timer 0 to SYSKLOCK/8 = 20MHz / 8 = 2.5MHz |
// i.e. the timer increments from 0x00 to 0xFF with an update rate of 2.5 MHz |
// hence the timer overflow interrupt frequency is 2.5 MHz / 256 = 9.765 kHz |
// divider 8 (Bits CS02 = 0, CS01 = 1, CS00 = 0) |
TCCR0B &= ~((1<<FOC0A)|(1<<FOC0B)|(1<<WGM02)); |
TCCR0B = (TCCR0B & 0xF8)|(0<<CS02)|(1<<CS01)|(0<<CS00); |
// init Timer/Counter 0 Register |
TCNT0 = 0; |
// Timer/Counter 0 Interrupt Mask Register |
// enable timer overflow interrupt only |
TIMSK0 &= ~((1<<OCIE0B)|(1<<OCIE0A)); |
TIMSK0 |= (1<<TOIE0); |
SystemTime.Year = 0; |
SystemTime.Month = 0; |
SystemTime.Day = 0; |
SystemTime.Hour = 0; |
SystemTime.Min = 0; |
SystemTime.Sec = 0; |
SystemTime.mSec = 0; |
SystemTime.Valid = 0; |
CountMilliseconds = 0; |
SREG = sreg; |
} |
/*****************************************************/ |
/* Interrupt Routine of Timer 0 */ |
/*****************************************************/ |
ISR(TIMER0_OVF_vect) // 9.765 kHz |
{ |
static uint8_t cnt = 0; |
#ifdef USE_FOLLOWME |
uint8_t Beeper_On = 0; |
#endif |
if(!cnt--) // every 10th run (9.765kHz/10 = 976Hz) |
{ |
cnt = 9; |
CountMilliseconds++; // increment millisecond counter |
} |
// beeper on if duration is not over |
if(BeepTime) |
{ |
BeepTime--; // decrement BeepTime |
if(BeepTime & BeepModulation) Beeper_On = 1; |
else Beeper_On = 0; |
} |
else // beeper off if duration is over |
{ |
Beeper_On = 0; |
BeepModulation = 0xFFFF; |
} |
#ifdef USE_FOLLOWME |
// if beeper is on |
if(Beeper_On) |
{ |
// set speaker port to high |
PORTC |= (1<<PORTC7); // Speaker at PC7 |
} |
else // beeper is off |
{ |
// set speaker port to low |
PORTC &= ~(1<<PORTC7);// Speaker at PC7 |
} |
#endif |
} |
// ----------------------------------------------------------------------- |
uint16_t SetDelay (uint16_t t) |
{ |
return(CountMilliseconds + t - 1); |
} |
// ----------------------------------------------------------------------- |
int8_t CheckDelay(uint16_t t) |
{ |
return(((t - CountMilliseconds) & 0x8000) >> 8); // check sign bit |
} |
// ----------------------------------------------------------------------- |
void Delay_ms(uint16_t w) |
{ |
unsigned int t_stop; |
t_stop = SetDelay(w); |
while (!CheckDelay(t_stop)); |
} |
/FollowMe/timer0.h |
---|
0,0 → 1,30 |
#ifndef _TIMER0_H |
#define _TIMER0_H |
#include <inttypes.h> |
typedef struct{ |
uint16_t Year; |
uint8_t Month; |
uint8_t Day; |
uint8_t Hour; |
uint8_t Min; |
uint8_t Sec; |
uint16_t mSec; |
uint8_t Valid; |
} DateTime_t; |
extern DateTime_t SystemTime; |
extern volatile uint16_t CountMilliseconds; |
extern volatile uint16_t BeepTime; |
extern volatile uint16_t BeepModulation; |
extern void TIMER0_Init(void); |
extern void Delay_ms(uint16_t w); |
extern void Delay_ms_Mess(uint16_t w); |
extern uint16_t SetDelay (uint16_t t); |
extern int8_t CheckDelay (uint16_t t); |
#endif //_TIMER0_H |
/FollowMe/uart0.c |
---|
0,0 → 1,522 |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 04.2007 Holger Buss |
// + only for non-profit use |
// + www.MikroKopter.com |
// + see the File "License.txt" for further Informations |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include <avr/wdt.h> |
#include <stdarg.h> |
#include <string.h> |
#include "main.h" |
#include "menu.h" |
#include "timer0.h" |
#include "uart0.h" |
#include "ubx.h" |
#include "led.h" |
#define FC_ADDRESS 1 |
#define NC_ADDRESS 2 |
#define MK3MAG_ADDRESS 3 |
#define FM_ADDRESS 10 // FOLLOW ME |
#define FALSE 0 |
#define TRUE 1 |
//int8_t test __attribute__ ((section (".noinit"))); |
uint8_t Request_VerInfo = FALSE; |
uint8_t Request_Display = FALSE; |
uint8_t Request_Display1 = FALSE; |
uint8_t Request_ExternalControl = FALSE; |
uint8_t Request_DebugData = FALSE; |
uint8_t Request_DebugLabel = 255; |
uint8_t Request_SendFollowMe = FALSE; |
uint8_t DisplayLine = 0; |
volatile uint8_t txd_buffer[TXD_BUFFER_LEN]; |
volatile uint8_t rxd_buffer_locked = FALSE; |
volatile uint8_t rxd_buffer[RXD_BUFFER_LEN]; |
volatile uint8_t txd_complete = TRUE; |
volatile uint8_t ReceivedBytes = 0; |
volatile uint8_t *pRxData = 0; |
volatile uint8_t RxDataLen = 0; |
uint8_t PcAccess = 100; |
ExternControl_t ExternControl; |
DebugOut_t DebugOut; |
UART_VersionInfo_t UART_VersionInfo; |
uint16_t DebugData_Timer; |
uint16_t DebugData_Interval = 500; // in 1ms |
Waypoint_t FollowMe; |
const uint8_t ANALOG_LABEL[32][16] = |
{ |
//1234567890123456 |
"Analog_Ch0 ", //0 |
"Analog_Ch1 ", |
"Analog_Ch2 ", |
"Analog_Ch3 ", |
"Analog_Ch4 ", |
"Analog_Ch5 ", //5 |
"Analog_Ch6 ", |
"Analog_Ch7 ", |
"UBat ", |
"Speed_North ", |
"Speed_East ", //10 |
"Speed_Top ", |
"NumOfSats ", |
"Pos.Longitude ", |
"Pos.Latitude ", |
"Pos.Altitude ", //15 |
"Zellenzahl ", |
"PowerOn ", |
"Debug18 ", |
"Debug19 ", |
"Debug20 ", //20 |
"Debug21 ", |
"Debug22 ", |
"Debug23 ", |
"Debug24 ", |
"Debug25 ", //25 |
"Debug26 ", |
"Debug27 ", |
"Debug28 ", |
"Debug29 ", |
"Debug30 ", //30 |
"Debug31 " |
}; |
/****************************************************************/ |
/* Initialization of the USART0 */ |
/****************************************************************/ |
void USART0_Init (void) |
{ |
uint8_t sreg = SREG; |
uint16_t ubrr = (uint16_t) ((uint32_t) SYSCLK/(8 * USART0_BAUD) - 1); |
// disable all interrupts before configuration |
cli(); |
// disable RX-Interrupt |
UCSR0B &= ~(1 << RXCIE0); |
// disable TX-Interrupt |
UCSR0B &= ~(1 << TXCIE0); |
// set direction of RXD0 and TXD0 pins |
// set RXD0 (PD0) as an input pin |
PORTD |= (1 << PORTD0); |
DDRD &= ~(1 << DDD0); |
// set TXD0 (PD1) as an output pin |
PORTD |= (1 << PORTD1); |
DDRD |= (1 << DDD1); |
// USART0 Baud Rate Register |
// set clock divider |
UBRR0H = (uint8_t)(ubrr >> 8); |
UBRR0L = (uint8_t)ubrr; |
// USART0 Control and Status Register A, B, C |
// enable double speed operation in |
UCSR0A |= (1 << U2X0); |
// enable receiver and transmitter in |
UCSR0B = (1 << TXEN0) | (1 << RXEN0); |
// set asynchronous mode |
UCSR0C &= ~(1 << UMSEL01); |
UCSR0C &= ~(1 << UMSEL00); |
// no parity |
UCSR0C &= ~(1 << UPM01); |
UCSR0C &= ~(1 << UPM00); |
// 1 stop bit |
UCSR0C &= ~(1 << USBS0); |
// 8-bit |
UCSR0B &= ~(1 << UCSZ02); |
UCSR0C |= (1 << UCSZ01); |
UCSR0C |= (1 << UCSZ00); |
// flush receive buffer |
while ( UCSR0A & (1<<RXC0) ) UDR0; |
// enable interrupts at the end |
// enable RX-Interrupt |
UCSR0B |= (1 << RXCIE0); |
// enable TX-Interrupt |
UCSR0B |= (1 << TXCIE0); |
// initialize the debug timer |
DebugData_Timer = SetDelay(DebugData_Interval); |
// unlock rxd_buffer |
rxd_buffer_locked = FALSE; |
pRxData = 0; |
RxDataLen = 0; |
// no bytes to send |
txd_complete = TRUE; |
UART_VersionInfo.SWMajor = VERSION_MAJOR; |
UART_VersionInfo.SWMinor = VERSION_MINOR; |
UART_VersionInfo.SWPatch = VERSION_PATCH; |
UART_VersionInfo.ProtoMajor = VERSION_SERIAL_MAJOR; |
UART_VersionInfo.ProtoMinor = VERSION_SERIAL_MINOR; |
// restore global interrupt flags |
SREG = sreg; |
} |
/****************************************************************/ |
/* USART0 transmitter ISR */ |
/****************************************************************/ |
ISR(USART0_TX_vect) |
{ |
static uint16_t ptr_txd_buffer = 0; |
uint8_t tmp_tx; |
if(!txd_complete) // transmission not completed |
{ |
ptr_txd_buffer++; // die [0] wurde schon gesendet |
tmp_tx = txd_buffer[ptr_txd_buffer]; |
// if terminating character or end of txd buffer was reached |
if((tmp_tx == '\r') || (ptr_txd_buffer == TXD_BUFFER_LEN)) |
{ |
ptr_txd_buffer = 0; // reset txd pointer |
txd_complete = 1; // stop transmission |
} |
UDR0 = tmp_tx; // send current byte will trigger this ISR again |
} |
// transmission completed |
else ptr_txd_buffer = 0; |
} |
/****************************************************************/ |
/* USART0 receiver ISR */ |
/****************************************************************/ |
ISR(USART0_RX_vect) |
{ |
static uint16_t crc; |
static uint8_t ptr_rxd_buffer = 0; |
uint8_t crc1, crc2; |
uint8_t c; |
c = UDR0; // catch the received byte |
if(rxd_buffer_locked) return; // if rxd buffer is locked immediately return |
// the rxd buffer is unlocked |
if((ptr_rxd_buffer == 0) && (c == '#')) // if rxd buffer is empty and syncronisation character is received |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy 1st byte to buffer |
crc = c; // init crc |
} |
#if 0 |
else if (ptr_rxd_buffer == 1) // handle address |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy byte to rxd buffer |
crc += c; // update crc |
} |
#endif |
else if (ptr_rxd_buffer < RXD_BUFFER_LEN) // collect incomming bytes |
{ |
if(c != '\r') // no termination character |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy byte to rxd buffer |
crc += c; // update crc |
} |
else // termination character was received |
{ |
// the last 2 bytes are no subject for checksum calculation |
// they are the checksum itself |
crc -= rxd_buffer[ptr_rxd_buffer-2]; |
crc -= rxd_buffer[ptr_rxd_buffer-1]; |
// calculate checksum from transmitted data |
crc %= 4096; |
crc1 = '=' + crc / 64; |
crc2 = '=' + crc % 64; |
// compare checksum to transmitted checksum bytes |
if((crc1 == rxd_buffer[ptr_rxd_buffer-2]) && (crc2 == rxd_buffer[ptr_rxd_buffer-1])) |
{ // checksum valid |
rxd_buffer[ptr_rxd_buffer] = '\r'; // set termination character |
ReceivedBytes = ptr_rxd_buffer + 1;// store number of received bytes |
rxd_buffer_locked = TRUE; // lock the rxd buffer |
// if 2nd byte is an 'R' enable watchdog that will result in an reset |
if(rxd_buffer[2] == 'R') {wdt_enable(WDTO_250MS);} // Reset-Commando |
} |
else |
{ // checksum invalid |
rxd_buffer_locked = FALSE; // unlock rxd buffer |
} |
ptr_rxd_buffer = 0; // reset rxd buffer pointer |
} |
} |
else // rxd buffer overrun |
{ |
ptr_rxd_buffer = 0; // reset rxd buffer |
rxd_buffer_locked = FALSE; // unlock rxd buffer |
} |
} |
// -------------------------------------------------------------------------- |
void AddCRC(uint16_t datalen) |
{ |
uint16_t tmpCRC = 0, i; |
for(i = 0; i < datalen; i++) |
{ |
tmpCRC += txd_buffer[i]; |
} |
tmpCRC %= 4096; |
txd_buffer[i++] = '=' + tmpCRC / 64; |
txd_buffer[i++] = '=' + tmpCRC % 64; |
txd_buffer[i++] = '\r'; |
txd_complete = FALSE; |
UDR0 = txd_buffer[0]; // initiates the transmittion (continued in the TXD ISR) |
} |
// -------------------------------------------------------------------------- |
void SendOutData(uint8_t cmd, uint8_t addr, uint8_t numofbuffers, ...) // uint8_t *pdata, uint8_t len, ... |
{ |
va_list ap; |
uint16_t pt = 0; |
uint8_t a,b,c; |
uint8_t ptr = 0; |
uint8_t *pdata = 0; |
int len = 0; |
txd_buffer[pt++] = '#'; // Start character |
txd_buffer[pt++] = 'a' + addr; // Address (a=0; b=1,...) |
txd_buffer[pt++] = cmd; // Command |
va_start(ap, numofbuffers); |
if(numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
while(len) |
{ |
if(len) |
{ |
a = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else a = 0; |
if(len) |
{ |
b = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else b = 0; |
if(len) |
{ |
c = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else c = 0; |
txd_buffer[pt++] = '=' + (a >> 2); |
txd_buffer[pt++] = '=' + (((a & 0x03) << 4) | ((b & 0xf0) >> 4)); |
txd_buffer[pt++] = '=' + (((b & 0x0f) << 2) | ((c & 0xc0) >> 6)); |
txd_buffer[pt++] = '=' + ( c & 0x3f); |
} |
va_end(ap); |
AddCRC(pt); // add checksum after data block and initates the transmission |
} |
// -------------------------------------------------------------------------- |
void Decode64(void) |
{ |
uint8_t a,b,c,d; |
uint8_t x,y,z; |
uint8_t ptrIn = 3; |
uint8_t ptrOut = 3; |
uint8_t len = ReceivedBytes - 6; |
while(len) |
{ |
a = rxd_buffer[ptrIn++] - '='; |
b = rxd_buffer[ptrIn++] - '='; |
c = rxd_buffer[ptrIn++] - '='; |
d = rxd_buffer[ptrIn++] - '='; |
//if(ptrIn > ReceivedBytes - 3) break; |
x = (a << 2) | (b >> 4); |
y = ((b & 0x0f) << 4) | (c >> 2); |
z = ((c & 0x03) << 6) | d; |
if(len--) rxd_buffer[ptrOut++] = x; else break; |
if(len--) rxd_buffer[ptrOut++] = y; else break; |
if(len--) rxd_buffer[ptrOut++] = z; else break; |
} |
pRxData = &rxd_buffer[3]; |
RxDataLen = ptrOut - 3; |
} |
// -------------------------------------------------------------------------- |
void USART0_ProcessRxData(void) |
{ |
// if data in the rxd buffer are not locked immediately return |
if(!rxd_buffer_locked) return; |
Decode64(); // decode data block in rxd_buffer |
switch(rxd_buffer[1] - 'a') |
{ |
case FM_ADDRESS: |
switch(rxd_buffer[2]) |
{ |
default: |
//unsupported command received |
break; |
} // case FC_ADDRESS: |
default: // any Slave Address |
switch(rxd_buffer[2]) |
{ |
case 'a':// request for labels of the analog debug outputs |
Request_DebugLabel = pRxData[0]; |
if(Request_DebugLabel > 31) Request_DebugLabel = 31; |
PcAccess = 255; |
break; |
case 'h':// request for display columns |
PcAccess = 255; |
RemoteKeys |= pRxData[0]; |
if(RemoteKeys) DisplayLine = 0; |
Request_Display = TRUE; |
break; |
case 'l':// request for display columns |
PcAccess = 255; |
MenuItem = pRxData[0]; |
Request_Display1 = TRUE; |
break; |
case 'v': // request for version and board release |
Request_VerInfo = TRUE; |
break; |
case 'd': // request for the debug data |
DebugData_Interval = (uint16_t) pRxData[0] * 10; |
if(DebugData_Interval > 0) Request_DebugData = TRUE; |
break; |
case 'g':// get external control data |
Request_ExternalControl = TRUE; |
break; |
default: |
//unsupported command received |
break; |
} |
break; // default: |
} |
// unlock the rxd buffer after processing |
pRxData = 0; |
RxDataLen = 0; |
rxd_buffer_locked = FALSE; |
} |
//############################################################################ |
//Routine für die Serielle Ausgabe |
int16_t uart_putchar (int8_t c) |
//############################################################################ |
{ |
if (c == '\n') |
uart_putchar('\r'); |
// wait until previous character was send |
loop_until_bit_is_set(UCSR0A, UDRE0); |
// send character |
UDR0 = c; |
return (0); |
} |
//--------------------------------------------------------------------------------------------- |
void USART0_TransmitTxData(void) |
{ |
if(!txd_complete) return; |
if(Request_VerInfo && txd_complete) |
{ |
SendOutData('V', FM_ADDRESS, 1, (uint8_t *) &UART_VersionInfo, sizeof(UART_VersionInfo)); |
Request_VerInfo = FALSE; |
} |
if(Request_Display && txd_complete) |
{ |
LCD_PrintMenu(); |
SendOutData('H', FM_ADDRESS, 2, &DisplayLine, sizeof(DisplayLine), &DisplayBuff[DisplayLine * 20], 20); |
DisplayLine++; |
if(DisplayLine >= 4) DisplayLine = 0; |
Request_Display = FALSE; |
} |
if(Request_Display1 && txd_complete) |
{ |
LCD_PrintMenu(); |
SendOutData('L', FM_ADDRESS, 3, &MenuItem, sizeof(MenuItem), &MaxMenuItem, sizeof(MaxMenuItem), DisplayBuff, sizeof(DisplayBuff)); |
Request_Display1 = FALSE; |
} |
if(Request_DebugLabel != 0xFF) // Texte für die Analogdaten |
{ |
SendOutData('A', FM_ADDRESS, 2, (uint8_t *) &Request_DebugLabel, sizeof(Request_DebugLabel), ANALOG_LABEL[Request_DebugLabel], 16); |
Request_DebugLabel = 0xFF; |
} |
if(Request_ExternalControl && txd_complete) |
{ |
SendOutData('G', FM_ADDRESS, 1,(uint8_t *) &ExternControl, sizeof(ExternControl)); |
Request_ExternalControl = FALSE; |
} |
if( ((DebugData_Interval && CheckDelay(DebugData_Timer)) || Request_DebugData) && txd_complete) |
{ |
SendOutData('D', FM_ADDRESS, 1,(uint8_t *) &DebugOut, sizeof(DebugOut)); |
DebugData_Timer = SetDelay(DebugData_Interval); |
Request_DebugData = FALSE; |
} |
if(Request_SendFollowMe && txd_complete) |
{ |
SendOutData('s', NC_ADDRESS, 1, (uint8_t *)&FollowMe, sizeof(FollowMe)); |
FollowMe.Position.Status = PROCESSED; |
Request_SendFollowMe = FALSE; |
} |
} |
/FollowMe/uart0.h |
---|
0,0 → 1,73 |
#ifndef _UART0_H |
#define _UART0_H |
#include "ubx.h" |
#define RXD_BUFFER_LEN 150 |
// must be at least 4('#'+Addr+'CmdID'+'\r')+ (80 * 4)/3 = 111 bytes |
#define TXD_BUFFER_LEN 150 |
#define RXD_BUFFER_LEN 150 |
#include <inttypes.h> |
//Baud rate of the USART |
#define USART0_BAUD 57600 |
extern void USART0_Init (void); |
extern void USART0_TransmitTxData(void); |
extern void USART0_ProcessRxData(void); |
extern int16_t uart_putchar(int8_t c); |
extern uint8_t PcAccess; |
extern uint8_t RemotePollDisplayLine; |
typedef struct |
{ |
uint8_t Digital[2]; |
uint8_t RemoteButtons; |
int8_t Nick; |
int8_t Roll; |
int8_t Yaw; |
uint8_t Gas; |
int8_t Height; |
uint8_t free; |
uint8_t Frame; |
uint8_t Config; |
} __attribute__((packed)) ExternControl_t; |
extern ExternControl_t ExternControl; |
typedef struct |
{ |
uint8_t Digital[2]; |
uint16_t Analog[32]; // Debugvalues |
} __attribute__((packed)) DebugOut_t; |
extern DebugOut_t DebugOut; |
typedef struct |
{ |
uint8_t SWMajor; |
uint8_t SWMinor; |
uint8_t ProtoMajor; |
uint8_t ProtoMinor; |
uint8_t SWPatch; |
uint8_t Reserved[5]; |
} __attribute__((packed)) UART_VersionInfo_t; |
typedef struct |
{ |
GPS_Pos_t Position; // the gps position of the waypoint, see ubx.h for details |
int16_t Heading; // orientation, future implementation |
uint8_t ToleranceRadius; // in meters, if the MK is within that range around the target, then the next target is triggered |
uint8_t HoldTime; // in seconds, if the was once in the tolerance area around a WP, this time defines the delay before the next WP is triggered |
uint8_t Event_Flag; // future implementation |
uint8_t reserve[12]; // reserve |
} __attribute__((packed)) Waypoint_t; |
extern Waypoint_t FollowMe; |
extern uint8_t Request_SendFollowMe; |
#endif //_UART0_H |
/FollowMe/uart1.c |
---|
0,0 → 1,94 |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "main.h" |
#include "uart1.h" |
#include "ubx.h" |
/****************************************************************/ |
/* Initialization of the USART1 */ |
/****************************************************************/ |
void USART1_Init (void) |
{ |
// USART1 Control and Status Register A, B, C and baud rate register |
uint8_t sreg = SREG; |
uint16_t ubrr = (uint16_t) ((uint32_t) SYSCLK/(8 * USART1_BAUD) - 1); |
// disable all interrupts before reconfiguration |
cli(); |
// disable RX-Interrupt |
UCSR1B &= ~(1 << RXCIE1); |
// disable TX-Interrupt |
UCSR1B &= ~(1 << TXCIE1); |
// disable DRE-Interrupt |
UCSR1B &= ~(1 << UDRIE1); |
// set direction of RXD1 and TXD1 pins |
// set RXD1 (PD2) as an input pin |
PORTD |= (1 << PORTD2); |
DDRD &= ~(1 << DDD2); |
// set TXD1 (PD3) as an output pin |
PORTD |= (1 << PORTD3); |
DDRD |= (1 << DDD3); |
// USART0 Baud Rate Register |
// set clock divider |
UBRR1H = (uint8_t)(ubrr>>8); |
UBRR1L = (uint8_t)ubrr; |
// enable double speed operation |
UCSR1A |= (1 << U2X1); |
// enable receiver and transmitter |
UCSR1B = (1 << TXEN1) | (1 << RXEN1); |
// set asynchronous mode |
UCSR1C &= ~(1 << UMSEL11); |
UCSR1C &= ~(1 << UMSEL10); |
// no parity |
UCSR1C &= ~(1 << UPM11); |
UCSR1C &= ~(1 << UPM10); |
// 1 stop bit |
UCSR1C &= ~(1 << USBS1); |
// 8-bit |
UCSR1B &= ~(1 << UCSZ12); |
UCSR1C |= (1 << UCSZ11); |
UCSR1C |= (1 << UCSZ10); |
// flush receive buffer explicit |
while ( UCSR1A & (1<<RXC1) ) UDR1; |
// enable interrupts at the end |
// enable RX-Interrupt |
UCSR1B |= (1 << RXCIE1); |
// enable TX-Interrupt |
UCSR1B |= (1 << TXCIE1); |
// enable DRE interrupt |
//UCSR1B |= (1 << UDRIE1); |
// restore global interrupt flags |
SREG = sreg; |
} |
/****************************************************************/ |
/* USART1 transmitter ISR */ |
/****************************************************************/ |
/*ISR(USART1_TX_vect) |
{ |
} |
*/ |
/****************************************************************/ |
/* USART1 receiver ISR */ |
/****************************************************************/ |
ISR(USART1_RX_vect) |
{ |
uint8_t c; |
c = UDR1; // get data byte |
UBX_Parser(c); // and put it into the ubx protocol parser |
} |
/FollowMe/uart1.h |
---|
0,0 → 1,25 |
#ifndef _UART1_H |
#define _UART1_H |
#define USART1_BAUD 57600 |
/* |
Initialize the USART und activate the receiver and transmitter |
as well as the receive-interrupt. The IO-FIFOs are initialized. |
The global interrupt-enable-flag (I-Bit in SREG) is not changed |
*/ |
extern void USART1_Init (void); |
/* |
The character c is stored in the output buffer. If the character was pushed sucessfully to |
the output buffer then the return value is 1. In case of an output buffer overflow the return value is 0. |
The isr is activated, which will send the data from the outbut buffer to the UART. |
*/ |
extern int USART1_putc (const uint8_t c); |
/* |
extern uint8_t USART1_getc_wait(void); |
extern int16_t USART1_getc_nowait(void); |
*/ |
#endif //_UART1_H |
/FollowMe/ubx.c |
---|
0,0 → 1,413 |
#include <inttypes.h> |
#include "ubx.h" |
#include "timer0.h" |
#include "uart0.h" |
// ------------------------------------------------------------------------------------------------ |
// defines |
#define DAYS_FROM_JAN01YEAR0001_TO_JAN6_1980 722819 // the year 0 does not exist! |
#define DAYS_PER_YEAR 365 |
#define DAYS_PER_LEAPYEAR 366 |
#define DAYS_PER_4YEARS 1461 //((3 * DAYS_PER_YEAR) + DAYS_PER_LEAPYEAR) // years dividable by 4 are leap years |
#define DAYS_PER_100YEARS 36524 //((25 * DAYS_PER_4YEARS) - 1) // years dividable by 100 are no leap years |
#define DAYS_PER_400YEARS 146097 //((4 * DAYS_PER_100YEARS) + 1L) // but years dividable by 400 are leap years |
#define SECONDS_PER_MINUTE 60 |
#define MINUTES_PER_HOUR 60 |
#define HOURS_PER_DAY 24 |
#define DAYS_PER_WEEK 7 |
#define SECONDS_PER_HOUR 3600 //(SECONDS_PER_MINUTE * MINUTES_PER_HOUR) |
#define SECONDS_PER_DAY 86400 //(SECONDS_PER_HOUR * HOURS_PER_DAY) |
#define SECONDS_PER_WEEK 604800 //(SECONDS_PER_DAY * DAYS_PER_WEEK) |
// days per month in normal and leap years |
const uint32_t Leap[ 13 ] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; |
const uint32_t Normal[ 13 ] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; |
#define LEAP_SECONDS_FROM_1980 15 // the last one was on the Dec 31th 2008 |
// message sync bytes |
#define UBX_SYNC1_CHAR 0xB5 |
#define UBX_SYNC2_CHAR 0x62 |
// protocoll identifier |
#define UBX_CLASS_NAV 0x01 |
// message id |
#define UBX_ID_POSLLH 0x02 |
#define UBX_ID_SOL 0x06 |
#define UBX_ID_VELNED 0x12 |
// ------------------------------------------------------------------------------------------------ |
// typedefs |
// ubx parser state |
typedef enum |
{ |
UBXSTATE_IDLE, |
UBXSTATE_SYNC1, |
UBXSTATE_SYNC2, |
UBXSTATE_CLASS, |
UBXSTATE_LEN1, |
UBXSTATE_LEN2, |
UBXSTATE_DATA, |
UBXSTATE_CKA, |
UBXSTATE_CKB |
} ubxState_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t frac; // ns remainder of rounded ms above |
int16_t week; // GPS week |
uint8_t GPSfix; // GPSfix Type, range 0..6 |
uint8_t Flags; // Navigation Status Flags |
int32_t ECEF_X; // cm ECEF X coordinate |
int32_t ECEF_Y; // cm ECEF Y coordinate |
int32_t ECEF_Z; // cm ECEF Z coordinate |
int32_t PAcc; // cm 3D Position Accuracy Estimate |
int32_t ECEFVX; // cm/s ECEF X velocity |
int32_t ECEFVY; // cm/s ECEF Y velocity |
int32_t ECEFVZ; // cm/s ECEF Z velocity |
uint32_t SAcc; // cm/s Speed Accuracy Estimate |
uint16_t PDOP; // 0.01 Position DOP |
uint8_t res1; // reserved |
uint8_t numSV; // Number of SVs used in navigation solution |
uint32_t res2; // reserved |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_sol_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t VEL_N; // cm/s NED north velocity |
int32_t VEL_E; // cm/s NED east velocity |
int32_t VEL_D; // cm/s NED down velocity |
int32_t Speed; // cm/s Speed (3-D) |
int32_t GSpeed; // cm/s Ground Speed (2-D) |
int32_t Heading; // 1e-05 deg Heading 2-D |
uint32_t SAcc; // cm/s Speed Accuracy Estimate |
uint32_t CAcc; // deg Course / Heading Accuracy Estimate |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_velned_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t LON; // 1e-07 deg Longitude |
int32_t LAT; // 1e-07 deg Latitude |
int32_t HEIGHT; // mm Height above Ellipsoid |
int32_t HMSL; // mm Height above mean sea level |
uint32_t Hacc; // mm Horizontal Accuracy Estimate |
uint32_t Vacc; // mm Vertical Accuracy Estimate |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_posllh_t; |
//------------------------------------------------------------------------------------ |
// global variables |
// local buffers for the incomming ubx messages |
volatile ubx_nav_sol_t UbxSol = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, INVALID}; |
volatile ubx_nav_posllh_t UbxPosLlh = {0,0,0,0,0,0,0, INVALID}; |
volatile ubx_nav_velned_t UbxVelNed = {0,0,0,0,0,0,0,0,0, INVALID}; |
uint16_t CheckGPSOkay = 0; |
// shared buffer |
gps_data_t GPSData = {{0,0,0,INVALID},0,0,0,0,0,0,0, INVALID}; |
//------------------------------------------------------------------------------------ |
// functions |
uint8_t IsLeapYear(uint16_t year) |
{ |
if((year%400 == 0) || ( (year%4 == 0) && (year%100 != 0) ) ) return 1; |
else return 0; |
} |
/********************************************************/ |
/* Calculates the UTC Time from the GPS week and tow */ |
/********************************************************/ |
void SetGPSTime(DateTime_t * pTimeStruct) |
{ |
uint32_t Days, Seconds, Week; |
uint16_t YearPart; |
uint32_t * MonthDayTab = 0; |
uint8_t i; |
// if GPS data show valid time data |
if((UbxSol.Status != INVALID) && (UbxSol.Flags & FLAG_WKNSET) && (UbxSol.Flags & FLAG_TOWSET) ) |
{ |
Seconds = UbxSol.itow / 1000L; |
Week = (uint32_t)UbxSol.week; |
// correct leap seconds since 1980 |
if(Seconds < LEAP_SECONDS_FROM_1980) |
{ |
Week--; |
Seconds = SECONDS_PER_WEEK - LEAP_SECONDS_FROM_1980 + Seconds; |
} |
else Seconds -= LEAP_SECONDS_FROM_1980; |
Days = DAYS_FROM_JAN01YEAR0001_TO_JAN6_1980; |
Days += (Week * DAYS_PER_WEEK); |
Days += Seconds / SECONDS_PER_DAY; // seperate days from GPS seconds of week |
pTimeStruct->Year = 1; |
YearPart = (uint16_t)(Days / DAYS_PER_400YEARS); |
pTimeStruct->Year += YearPart * 400; |
Days = Days % DAYS_PER_400YEARS; |
YearPart = (uint16_t)(Days / DAYS_PER_100YEARS); |
pTimeStruct->Year += YearPart * 100; |
Days = Days % DAYS_PER_100YEARS; |
YearPart = (uint16_t)(Days / DAYS_PER_4YEARS); |
pTimeStruct->Year += YearPart * 4; |
Days = Days % DAYS_PER_4YEARS; |
if(Days < (3* DAYS_PER_YEAR)) YearPart = (uint16_t)(Days / DAYS_PER_YEAR); |
else YearPart = 3; |
pTimeStruct->Year += YearPart; |
// calculate remaining days of year |
Days -= (uint32_t)(YearPart * DAYS_PER_YEAR); |
Days += 1; |
// check if current year is a leap year |
if(IsLeapYear(pTimeStruct->Year)) MonthDayTab = (uint32_t*)Leap; |
else MonthDayTab = (uint32_t*)Normal; |
// seperate month and day from days of year |
for ( i = 0; i < 12; i++ ) |
{ |
if ( (MonthDayTab[i]< Days) && (Days <= MonthDayTab[i+1]) ) |
{ |
pTimeStruct->Month = i+1; |
pTimeStruct->Day = Days - MonthDayTab[i]; |
i = 12; |
} |
} |
Seconds = Seconds % SECONDS_PER_DAY; // remaining seconds of current day |
pTimeStruct->Hour = (uint8_t)(Seconds / SECONDS_PER_HOUR); |
Seconds = Seconds % SECONDS_PER_HOUR; // remaining seconds of current hour |
pTimeStruct->Min = (uint8_t)(Seconds / SECONDS_PER_MINUTE); |
Seconds = Seconds % SECONDS_PER_MINUTE; // remaining seconds of current minute |
pTimeStruct->Sec = (uint8_t)(Seconds); |
pTimeStruct->mSec = (uint16_t)(UbxSol.itow % 1000L); |
pTimeStruct->Valid = 1; |
} |
else |
{ |
pTimeStruct->Valid = 0; |
} |
} |
/********************************************************/ |
/* Initialize UBX Parser */ |
/********************************************************/ |
void UBX_Init(void) |
{ |
// mark msg buffers invalid |
UbxSol.Status = INVALID; |
UbxPosLlh.Status = INVALID; |
UbxVelNed.Status = INVALID; |
GPSData.Status = INVALID; |
} |
/********************************************************/ |
/* Upate GPS data stcructure */ |
/********************************************************/ |
void Update_GPSData (void) |
{ |
static uint16_t Ubx_Timeout = 0; |
static uint8_t Msg_Count = 0; |
// the timeout is used to detect the delay between two message sets |
// and is used for synchronisation so that always a set is collected |
// that belongs together |
// _______NAVSOL|POSLLH|VELNED|___________________NAVSOL|POSLLH|VELNED|_____________ |
// | 8ms | 8ms | 184 ms | | | |
// msg_count: 0 1 2 0 1 2 |
if(CheckDelay(Ubx_Timeout)) Msg_Count = 0; |
else Msg_Count++; |
Ubx_Timeout = SetDelay(100); // reset ubx msg timeout |
// if a new set of ubx messages was collected |
if((Msg_Count >= 2)) |
{ // if set is complete |
if((UbxSol.Status == NEWDATA) && (UbxPosLlh.Status == NEWDATA) && (UbxVelNed.Status == NEWDATA)) |
{ |
CheckGPSOkay++; |
// update GPS data only if the status is INVALID or PROCESSED and the last ubx message was received within less than 100 ms |
if(GPSData.Status != NEWDATA) // if last data were processed |
{ // wait for new data at all neccesary ubx messages |
GPSData.Status = INVALID; |
// NAV SOL |
GPSData.Flags = UbxSol.Flags; |
GPSData.NumOfSats = UbxSol.numSV; |
GPSData.SatFix = UbxSol.GPSfix; |
GPSData.Position_Accuracy = UbxSol.PAcc; |
GPSData.Speed_Accuracy = UbxSol.SAcc; |
SetGPSTime(&SystemTime); // update system time |
// NAV POSLLH |
GPSData.Position.Status = INVALID; |
GPSData.Position.Longitude = UbxPosLlh.LON; |
GPSData.Position.Latitude = UbxPosLlh.LAT; |
GPSData.Position.Altitude = UbxPosLlh.HMSL; |
GPSData.Position.Status = NEWDATA; |
// NAV VELNED |
GPSData.Speed_East = UbxVelNed.VEL_E; |
GPSData.Speed_North = UbxVelNed.VEL_N; |
GPSData.Speed_Top = -UbxVelNed.VEL_D; |
GPSData.Speed_Ground = UbxVelNed.GSpeed; |
GPSData.Heading = UbxVelNed.Heading; |
GPSData.Status = NEWDATA; // new data available |
} // EOF if(GPSData.Status != NEWDATA) |
} // EOF all ubx messages received |
// set state to collect new data |
UbxSol.Status = PROCESSED; // ready for new data |
UbxPosLlh.Status = PROCESSED; // ready for new data |
UbxVelNed.Status = PROCESSED; // ready for new data |
} |
} |
/********************************************************/ |
/* UBX Parser */ |
/********************************************************/ |
void UBX_Parser(uint8_t c) |
{ |
static ubxState_t ubxState = UBXSTATE_IDLE; |
static uint16_t msglen; |
static uint8_t cka, ckb; |
static uint8_t *ubxP, *ubxEp, *ubxSp; // pointers to data currently transfered |
//state machine |
switch (ubxState) // ubx message parser |
{ |
case UBXSTATE_IDLE: // check 1st sync byte |
if (c == UBX_SYNC1_CHAR) ubxState = UBXSTATE_SYNC1; |
else ubxState = UBXSTATE_IDLE; // out of synchronization |
break; |
case UBXSTATE_SYNC1: // check 2nd sync byte |
if (c == UBX_SYNC2_CHAR) ubxState = UBXSTATE_SYNC2; |
else ubxState = UBXSTATE_IDLE; // out of synchronization |
break; |
case UBXSTATE_SYNC2: // check msg class to be NAV |
if (c == UBX_CLASS_NAV) ubxState = UBXSTATE_CLASS; |
else ubxState = UBXSTATE_IDLE; // unsupported message class |
break; |
case UBXSTATE_CLASS: // check message identifier |
switch(c) |
{ |
case UBX_ID_POSLLH: // geodetic position |
ubxP = (uint8_t *)&UbxPosLlh; // data start pointer |
ubxEp = (uint8_t *)(&UbxPosLlh + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxPosLlh.Status; // status pointer |
break; |
case UBX_ID_SOL: // navigation solution |
ubxP = (uint8_t *)&UbxSol; // data start pointer |
ubxEp = (uint8_t *)(&UbxSol + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxSol.Status; // status pointer |
break; |
case UBX_ID_VELNED: // velocity vector in tangent plane |
ubxP = (uint8_t *)&UbxVelNed; // data start pointer |
ubxEp = (uint8_t *)(&UbxVelNed + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxVelNed.Status; // status pointer |
break; |
default: // unsupported identifier |
ubxState = UBXSTATE_IDLE; |
break; |
} |
if (ubxState != UBXSTATE_IDLE) |
{ |
ubxState = UBXSTATE_LEN1; |
cka = UBX_CLASS_NAV + c; |
ckb = UBX_CLASS_NAV + cka; |
} |
break; |
case UBXSTATE_LEN1: // 1st message length byte |
msglen = (uint16_t)c; // lowbyte first |
cka += c; |
ckb += cka; |
ubxState = UBXSTATE_LEN2; |
break; |
case UBXSTATE_LEN2: // 2nd message length byte |
msglen += ((uint16_t)c)<<8; // high byte last |
cka += c; |
ckb += cka; |
// if the old data are not processed so far then break parsing now |
// to avoid writing new data in ISR during reading by another function |
if ( *ubxSp == NEWDATA ) |
{ |
ubxState = UBXSTATE_IDLE; |
Update_GPSData(); //update GPS info respectively |
} |
else // data invalid or allready processd |
{ |
*ubxSp = INVALID; // mark invalid during buffer filling |
ubxState = UBXSTATE_DATA; |
} |
break; |
case UBXSTATE_DATA: // collecting data |
if (ubxP < ubxEp) |
{ |
*ubxP++ = c; // copy curent data byte if any space is left |
cka += c; |
ckb += cka; |
if (--msglen == 0) ubxState = UBXSTATE_CKA; // switch to next state if all data was read |
} |
else // rx buffer overrun |
{ |
ubxState = UBXSTATE_IDLE; |
} |
break; |
case UBXSTATE_CKA: |
if (c == cka) ubxState = UBXSTATE_CKB; |
else |
{ |
*ubxSp = INVALID; |
ubxState = UBXSTATE_IDLE; |
} |
break; |
case UBXSTATE_CKB: |
if (c == ckb) |
{ |
*ubxSp = NEWDATA; // new data are valid |
Update_GPSData(); //update GPS info respectively |
} |
else |
{ // if checksum not match then set data invalid |
*ubxSp = INVALID; |
} |
ubxState = UBXSTATE_IDLE; // ready to parse new data |
break; |
default: // unknown ubx state |
ubxState = UBXSTATE_IDLE; |
break; |
} |
DebugOut.Analog[9] = GPSData.Speed_North; |
DebugOut.Analog[10] = GPSData.Speed_East; |
DebugOut.Analog[11] = GPSData.Speed_Top; |
DebugOut.Analog[12] = GPSData.NumOfSats; |
DebugOut.Analog[13] = GPSData.Position.Longitude; |
DebugOut.Analog[14] = GPSData.Position.Latitude; |
DebugOut.Analog[15] = GPSData.Position.Altitude; |
} |
/FollowMe/ubx.h |
---|
0,0 → 1,57 |
#ifndef _UBX_H |
#define _UBX_H |
#include <inttypes.h> |
// Satfix types for GPSData.SatFix |
#define SATFIX_NONE 0x00 |
#define SATFIX_DEADRECKOING 0x01 |
#define SATFIX_2D 0x02 |
#define SATFIX_3D 0x03 |
#define SATFIX_GPS_DEADRECKOING 0x04 |
#define SATFIX_TIMEONLY 0x05 |
// Flags for interpretation of the GPSData.Flags |
#define FLAG_GPSFIXOK 0x01 // (i.e. within DOP & ACC Masks) |
#define FLAG_DIFFSOLN 0x02 // (is DGPS used) |
#define FLAG_WKNSET 0x04 // (is Week Number valid) |
#define FLAG_TOWSET 0x08 // (is Time of Week valid) |
#define INVALID 0x00 |
#define NEWDATA 0x01 |
#define PROCESSED 0x02 |
typedef struct |
{ |
int32_t Longitude; // in 1E-7 deg |
int32_t Latitude; // in 1E-7 deg |
int32_t Altitude; // in mm |
uint8_t Status; // validity of data |
} __attribute__((packed)) GPS_Pos_t; |
typedef struct |
{ |
GPS_Pos_t Position; // Lat/Lon/Alt |
uint8_t Flags; // Status Flags |
uint8_t NumOfSats; // number of satelites |
uint8_t SatFix; // type of satfix |
uint32_t Position_Accuracy; // in cm 3d position accuracy |
int32_t Speed_North; // in cm/s |
int32_t Speed_East; // in cm/s |
int32_t Speed_Top; // in cm/s |
uint32_t Speed_Ground; // 2D ground speed in cm/s |
int32_t Heading; // 1e-05 deg Heading 2-D (curent flight direction) |
uint32_t Speed_Accuracy; // in cm/s 3d velocity accuracy |
uint8_t Status; // status of data |
} __attribute__((packed)) gps_data_t; |
// The data are valid if the GPSData.Status is NEWDATA or PROCESSED. |
// To achieve new data after reading the GPSData.Status should be set to PROCESSED. |
extern gps_data_t GPSData; |
extern uint16_t CheckGPSOkay; |
void UBX_Init(void); |
void UBX_Parser(uint8_t c); |
#endif // _UBX_H |