| /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 |