From a8330158a955ed5e6d2b6778e0e5faf8bae9cd26 Mon Sep 17 00:00:00 2001 From: isavitsky Date: Sun, 30 Sep 2018 15:57:38 +0300 Subject: [PATCH] Initial commit --- CBUF.h | 238 ++++++ README.md | 12 + STM8L-433T30D-TNC.hex | 1184 +++++++++++++++++++++++++++ bridge.c | 115 +++ bridge.h | 7 + config.h | 187 +++++ intr.c | 78 ++ intr.h | 5 + kiss.c | 335 ++++++++ kiss.h | 27 + main.c | 268 ++++++ menue.c | 1800 +++++++++++++++++++++++++++++++++++++++++ menue.h | 7 + rfm98w.c | 83 ++ rfm98w.h | 260 ++++++ spi.c | 53 ++ spi.h | 21 + timer.c | 128 +++ timer.h | 22 + uart.c | 139 ++++ uart.h | 30 + util.c | 71 ++ util.h | 12 + 23 files changed, 5082 insertions(+) create mode 100644 CBUF.h create mode 100644 README.md create mode 100644 STM8L-433T30D-TNC.hex create mode 100644 bridge.c create mode 100644 bridge.h create mode 100644 config.h create mode 100644 intr.c create mode 100644 intr.h create mode 100644 kiss.c create mode 100644 kiss.h create mode 100644 main.c create mode 100644 menue.c create mode 100644 menue.h create mode 100644 rfm98w.c create mode 100644 rfm98w.h create mode 100644 spi.c create mode 100644 spi.h create mode 100644 timer.c create mode 100644 timer.h create mode 100644 uart.c create mode 100644 uart.h create mode 100644 util.c create mode 100644 util.h diff --git a/CBUF.h b/CBUF.h new file mode 100644 index 0000000..06ce9c0 --- /dev/null +++ b/CBUF.h @@ -0,0 +1,238 @@ +/**************************************************************************** +* +* Since this code originated from code which is public domain, I +* hereby declare this code to be public domain as well. +* +* Dave Hylands - dhylands@gmail.com +* +****************************************************************************/ +/** +* +* @file CBUF.h +* +* @defgroup CBUF Circular Buffer +* @{ +* +* @brief A simple and efficient set of circular buffer manipulations. +* +* These macros implement a circular buffer which employs get and put +* pointers, in such a way that mutual exclusion is not required +* (assumes one reader & one writer). +* +* It requires that the circular buffer size be a power of two, and the +* size of the buffer needs to smaller than the index. So an 8 bit index +* supports a circular buffer upto ( 1 << 7 ) = 128 entries, and a 16 bit index +* supports a circular buffer upto ( 1 << 15 ) = 32768 entries. +* +* The basis for these routines came from an article in Jack Ganssle's +* Embedded Muse: http://www.ganssle.com/tem/tem110.pdf +* +* In order to offer the most amount of flexibility for embedded environments +* you need to define a macro for the size. +* +* First, you need to name your circular buffer. For this example, we'll +* call it @c myQ. +* +* The size macro that needs to be defined will be the name of the +* circular buffer followed by @c _SIZE. The size must be a power of two +* and it needs to fit in the get/put indicies. i.e. if you use an +* 8 bit index, then the maximum supported size would be 128. +* +* The structure which defines the circular buffer needs to have 3 members +* m_getIdx, m_putIdx, and @c m_entry. +* +* @c m_getIdx and @c m_putIdx need to be unsigned integers of the same size. +* +* @c m_entry needs to be an array of @c xxx_SIZE entries, or a pointer to an +* array of @c xxx_SIZE entries. The type of each entry is entirely up to the +* caller. +* +* @code +* #define myQ_SIZE 64 +* +* volatile struct +* { +* uint8_t m_getIdx; +* uint8_t m_putIdx; +* uint8_t m_entry[ myQ_SIZE ]; +* +* } myQ; +* @endcode +* +* You could then use CBUF_Push to add a character to the circular buffer: +* +* @code +* CBUF_Push( myQ, 'x' ); +* @endcode +* +* And CBUF_Pop to retrieve an element from the buffer: +* +* @code +* ch = CBUF_Pop( myQ ); +* @endcode +* +* If you happen to prefer to use C++ instead, there is a templatized +* version which requires no macros. You just declare 3 template parameters: +* +* - The type that should be used for the index +* - The size of the circular buffer +* - The type that should be used for the entry +* +* For example: +* @code +* volatile CBUF< uint8_t, 64, char > myQ; +* @endcode +* +****************************************************************************/ + +#if !defined( CBUF_H ) +#define CBUF_H /**< Include Guard */ + +/* ---- Include Files ---------------------------------------------------- */ + +/* ---- Constants and Types ---------------------------------------------- */ + +/** +* Initializes the circular buffer for use. +*/ + +#define CBUF_Init( cbuf ) cbuf.m_getIdx = cbuf.m_putIdx = 0 + +/** +* Returns the number of elements which are currently +* contained in the circular buffer. +*/ + +//#define CBUF_Len( cbuf ) ((typeof( cbuf.m_putIdx ))(( cbuf.m_putIdx ) - ( cbuf.m_getIdx ))) +#define CBUF_Len( cbuf ) (( cbuf.m_putIdx ) - ( cbuf.m_getIdx )) + +/** +* Appends an element to the end of the circular buffer. The +* element is expected to be of the same type as the @c m_entry +* member. +*/ + +#define CBUF_Push( cbuf, elem ) (cbuf.m_entry)[ cbuf.m_putIdx++ & (( cbuf##_SIZE ) - 1 )] = (elem) + +/** +* Retrieves an element from the beginning of the circular buffer +*/ + +#define CBUF_Pop( cbuf ) (cbuf.m_entry)[ cbuf.m_getIdx++ & (( cbuf##_SIZE ) - 1 )] + +/** +* Returns a pointer to the last spot that was pushed. +*/ + +#define CBUF_GetLastEntryPtr( cbuf ) &(cbuf.m_entry)[ ( cbuf.m_putIdx - 1 ) & (( cbuf##_SIZE ) - 1 )] + +/** +* Returns a pointer to the next spot to push. This can be used +* in conjunction with CBUF_AdvancePushIdx to fill out an entry +* before indicating that it's available. It is the caller's +* responsibility to enure that space is available, and that no +* other items are pushed to overwrite the entry returned. +*/ + +#define CBUF_GetPushEntryPtr( cbuf ) &(cbuf.m_entry)[ cbuf.m_putIdx & (( cbuf##_SIZE ) - 1 )] + +/** +* Advances the put index. This is useful if you need to +* reserve space for an item but can't fill in the contents +* yet. CBUG_GetLastEntryPtr can be used to get a pointer to +* the item. It is the caller's responsibility to ensure that +* the item isn't popped before the contents are filled in. +*/ + +#define CBUF_AdvancePushIdx( cbuf ) cbuf.m_putIdx++ + +/** +* Advances the get index. This is slightly more efficient than +* popping and tossing the result. +*/ + +#define CBUF_AdvancePopIdx( cbuf ) cbuf.m_getIdx++ + +/** +* Retrieves the idx'th element from the beginning of +* the circular buffer +*/ + +#define CBUF_Get( cbuf, idx ) (cbuf.m_entry)[( cbuf.m_getIdx + idx ) & (( cbuf##_SIZE ) - 1 )] + +/** +* Retrieves the idx'th element from the end of the +* circular buffer. +*/ + +#define CBUF_GetEnd( cbuf, idx ) (cbuf.m_entry)[( cbuf.m_putIdx - idx - 1 ) & (( cbuf##_SIZE ) - 1 )] + +/** +* Returns a pointer to the next spot to push. +*/ + +#define CBUF_GetPopEntryPtr( cbuf ) &(cbuf.m_entry)[ cbuf.m_getIdx & (( cbuf##_SIZE ) - 1 )] + +/** +* Determines if the circular buffer is empty. +*/ + +#define CBUF_IsEmpty( cbuf ) ( CBUF_Len( cbuf ) == 0 ) + +/** +* Determines if the circular buffer is full. +*/ + +#define CBUF_IsFull( cbuf ) ( CBUF_Len( cbuf ) == ( cbuf##_SIZE )) + +/** +* Determines if the circular buffer is currenly overflowed or underflowed. +*/ + +#define CBUF_Error( cbuf ) ( CBUF_Len( cbuf ) > cbuf##_SIZE ) + +#if defined( __cplusplus ) + +template < class IndexType, unsigned Size, class EntryType > +class CBUF +{ +public: + + CBUF() + { + m_getIdx = m_putIdx = 0; + } + + IndexType Len() const { return m_putIdx - m_getIdx; } + + bool IsEmpty() const { return Len() == 0; } + bool IsFull() const { return Len() == Size; } + bool Error() const { return Len() > Size; } + + void Push( EntryType val ) + { + m_entry[ m_putIdx++ & ( Size - 1 )] = val; + } + + EntryType Pop() + { + return m_entry[ m_getIdx++ & ( Size - 1 )]; + } + +private: + + volatile IndexType m_getIdx; + volatile IndexType m_putIdx; + EntryType m_entry[ Size ]; + +}; + +#endif // __cplusplus + +/* ---- Variable Externs ------------------------------------------------- */ +/* ---- Function Prototypes ---------------------------------------------- */ + +/** @} */ + +#endif // CBUF_H + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4d886f --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# 433T30D-LoRa-KISS-TNC + +This project aims to make a better firmware for E32-DTU 433L30 LoRa data transceiver devices that can be actually used by radio amateurs as a cheap replacement for traditional 1200 baud AFSK TNCs. + +LoRa employs a chirp modulation which delivers a better path power budget. + +To install new firmware you will need to have an ST-LINK flasher and make some hardware changes to E32-DTU device that will void your warranty. + +More details are on Wiki. + +All hardware manipulations are on your own risk! + diff --git a/STM8L-433T30D-TNC.hex b/STM8L-433T30D-TNC.hex new file mode 100644 index 0000000..00321cf --- /dev/null +++ b/STM8L-433T30D-TNC.hex @@ -0,0 +1,1184 @@ +:1080000082009100820097A8820097A8820097A81A +:10801000820097A8820097A8820097A8820097A85C +:10802000820097A8820097A8820097A8820097A84C +:10803000820097A8820097A8820097A88200C66154 +:108040008200C9D1820097A8820097A8820097A8D1 +:10805000820097A88200C3B9820097A88200C7AFA8 +:10806000820097A88200C4F0820097A8820097A897 +:10807000820097A8820097A88200C001820097A87A +:1080800000100000978A00977200977800977E0092 +:10809000975600975D00976400976B0096350095A2 +:1080A000E10095ED0095F9009464009472009480CD +:1080B0000091170091DD00912D0092590097160054 +:1080C00096C50096CE0096D70096610096E0009681 +:1080D000E900971E00972600972E0093EC0095521A +:1080E00000955F0093FB00940A0094190094280007 +:1080F000948E00949C0094AA0094B80094C60094B6 +:10810000D40094E20094F00094FE008F9700966BE8 +:1081100000967500967F009689008FB0008C480409 +:108120000400100100041401000514003C061400B2 +:0D8130000000888C009C008080065000003C +:10813D008D008BF12000B604A480B800B700CD836C +:10814D00642071206D206E2071207472F90172A26D +:10815D00007E17015F894B0090AE00034B0090E647 +:10816D0004272C3D032710BE024272FB031F03247C +:10817D00060C0226020C013D02270CBE014272FBC9 +:10818D00021F0224020C01BE004272FB011F01906E +:10819D005A26C95B02320003849085905D2702AA9E +:1081AD000190855D2B0648390359905ABF0193CC38 +:1081BD0083ED27038725FDCC844B5B02CC843F5F89 +:1081CD00380056BF005FBF025B02878726FDCC8457 +:1081DD004B8D008BF12000B604A480B800B700CD04 +:1081ED00836420EA20E520E420D520CE72F2011729 +:1081FD00013F04BE02905FB6019097B306B205240D +:10820D00095890590C0226020C0172B00006240286 +:10821D00905A72B200044FB70235010003589059BD +:10822D0090B3042602B306250C72B000062402900A +:10823D005A72B20004390339024924E1330333027F +:10824D0043509059AE007F72F0015B022D26A300C2 +:10825D00FF2503CC843F02905049380056BF0036AD +:10826D00023603240C4D27093C0326053C0226014A +:10827D005CBF0087B70190504F46993601360236E4 +:10828D000346CC83EDBE01B6005849A07F2529A138 +:10829D00102509AE7FFF3D002A015C87995640AB42 +:1082AD000FA1082509884F0184A008200154A001C1 +:1082BD0024FB3D002A0150875F87B60148B600496F +:1082CD002542A07F253EA12025075F5ABF00BF0292 +:1082DD0087721E00014501004502014503023F035F +:1082ED0040AB1F200D4502034501024500013F0033 +:1082FD00A008A10824EF2008340036013602360309 +:10830D004A2AF5875FBF00BF02874FBE002604BE15 +:10831D00022743AE009E3D0020095A380339023929 +:10832D000139002AF572030002047210000372581D +:10833D000000489F463100004631000131000231F6 +:10834D000003AB7F24103C03260C3C0226083C01A5 +:10835D0026043C002600879085B60148B6004927C3 +:10836D0030A1FF271C721E0001884B00B60548B6D0 +:10837D000449273EA1FF2733721E00055F9751EC7C +:10838D000AAD4B2648AD4F2240250290FC5D90EC86 +:10839D0002AD3B270E5F5C5A3803390239012AF7CB +:1083AD008920C9AD3122225D90EC04AD29221890AF +:1083BD00EC06AD225D270D5F5C5A3807390639058D +:1083CD002AF720BA90EC085B02AC00874387B60110 +:1083DD0048BA02BA0381BE0650BE0459A3FF0081FC +:1083ED005D9C2C24A3FFE72C0D4FB703B702B701FB +:1083FD00380046B70087340136023603462402AAF8 +:10840D00015CA3000126EF5F7201000302AA01A324 +:10841D0000FE2C1E380138004146B700360141A13F +:10842D0081250E3C03260A3C0226063C0126023C11 +:10843D000087AEFF00380056BF005FBF02875F5A4E +:10844D00BF00BF0287BE005089BE0250BF02852704 +:10845D00015ABF0087AE0004B603EB03B703B602A3 +:10846D00E902B702B601E901B701B600F9B7008715 +:10847D00B600BE06429FB700B601BE05429FBB00C7 +:10848D00B700B602BE04429FBB00B700B603BE03E1 +:10849D00429FBB00B700B601BE06423F0172BB0052 +:1084AD0000BF00B602BE054272BB0000BF00B6039E +:1084BD00BE044272BB0000BF00B602BE06423F02C0 +:1084CD0072BB0001BF0124023C00B603BE0542721F +:1084DD00BB0001BF0124023C00B603BE06423F03B0 +:1084ED0072BB0002BF022405BE005CBF0087BE0048 +:1084FD00261E90BE04260CBE0290BE0665BF0290DD +:10850D00BF0687BF04450206450307BF00BF0287AC +:10851D00A6203D00260DBE01BF004503023F033AD4 +:10852D0003A0085F905F992017905959B304260353 +:10853D0090B306250B72B2000624015A72B00004E6 +:10854D0039033902390139004A2ADEBF0490BF06CA +:10855D00BE0053BF00BE0253BF02878D008BF120BA +:10856D00048D008BF14B004B003D002A0BAE00003B +:10857D008D0085AD030103023D042A09AE00048D73 +:10858D000085AD03018D0084FB844D2707AE0000EF +:10859D008D0085AD844D2707AE00048D0085AD8718 +:1085AD0060034FE202E7024FE201E7014FF2F78766 +:1085BD00BE00B3042610BE02B3068A84A47BA501B7 +:1085CD002702AA04888687AE0004B600FAB700B663 +:1085DD0001EA01B701B602EA02B702B603EA03B730 +:1085ED000387B600F8B700B601E801B701B602E897 +:1085FD0002B702B603E803B70387AE00004D270AA2 +:10860D00680369026901794A26F687AE00004D2795 +:10861D000A746601660266034A26F687BF025F3D4D +:10862D00022A0153BF008788A6082000887B028894 +:10863D007B02891E061F045F977B086B06FE1F07D2 +:10864D00858487891E06BF0820001E041F061E0292 +:10865D001F04855B0287887B03887B03887B0388E7 +:10866D007B076B04B6096B05B60A6B06B60B6B0779 +:10867D008487887B03887B03887B03887B076B0457 +:10868D00B60D6B05B60E6B06B60F6B078487887B30 +:10869D0005B7097B06B70A7B07B70B200F887B054B +:1086AD00B70D7B06B70E7B07B70F20007B046B075A +:1086BD00846B03846B03846B03848788A600200F6F +:1086CD0088A604200A88A608200588A60C20008804 +:1086DD00897B04887B04891E081F045F977B0A6BC6 +:1086ED00061F09FE1F071E095C5CFE1F0985848796 +:1086FD00891E06BF001E08BF022021891E06BF0469 +:10870D001E08BF062016891E06BF081E08BF0A20B8 +:10871D000B891E06BF0C1E08BF0E20001E031F076F +:10872D001E041F08855B04878D00871E8D0087132F +:10873D00878D008650874504004505014506024595 +:10874D00070387450800450901450A02450B0387C4 +:10875D00450D01450E02450F0387450004450105F2 +:10876D0045020645030787450804450905450A06E0 +:10877D00450B0787450C04450D05450E06450F07AE +:10878D008745000845010945020A45030B87450445 +:10879D000845050945060A45070B8745010D4502A4 +:1087AD000E45030F8745090D450A0E450B0F874EE4 +:1087BD006F746520746861742070203D203120280D +:1087CD0050203D2032353529206D65616E7320278F +:1087DD007472616E736D697420617320736F6F6E47 +:1087ED0020617320746865206368616E6E656C200E +:1087FD00636C656172732E27002854686973207746 +:10880D00696C6C206F766572777269746520746817 +:10881D00652070726576696F75736C792073746FEE +:10882D007265642073657474696E677320696E2058 +:10883D00544E4320454550524F4D2E2900456E74E0 +:10884D0065722052656750706D436F727265637407 +:10885D00696F6E20696E2072616E676520302E2EF5 +:10886D00323535206F7220686974205B454E544552 +:10887D00525D20746F2072657475726E3A2000BF60 +:10888D000092CE00BF02BE001C000290BE02260167 +:10889D00878D008F3A1C0003BF0092CE009093BECF +:1088AD00001C0002BF0092BC000590F75F5C72BB1C +:1088BD000006BF06905CBE025ABF0227C420E731F6 +:1088CD00202D20456E61626C65643B206D616E6488 +:1088DD006174656420666F72207768656E207468B8 +:1088ED00652073796D626F6C206C656E677468209E +:1088FD00657863656564732031366D7300456E74FC +:10890D00657220502D706172616D6574657220699C +:10891D006E2072616E676520302E2E323535206FD8 +:10892D007220686974205B454E5445525D20746F0A +:10893D002072657475726E3A2000456E74657220F2 +:10894D00507265616D626C6520696E2072616E6733 +:10895D006520302E2E3635353335206F72206869FF +:10896D0074205B454E5445525D20746F20726574C2 +:10897D0075726E3A2000456E74657220536C6F747B +:10898D0054696D6520696E2072616E676520302EA9 +:10899D002E323535206F7220686974205B454E5438 +:1089AD0045525D20746F2072657475726E3A2000A9 +:1089BD00456E7465722053796E63576F72642069CA +:1089CD006E2072616E676520302E2E323535206F28 +:1089DD007220686974205B454E5445525D20746F5A +:1089ED002072657475726E3A2000456E7465722042 +:1089FD00547844656C617920696E2072616E67658B +:108A0D0020302E2E323535206F7220686974205B30 +:108A1D00454E5445525D20746F2072657475726EAB +:108A2D003A2000456E746572204F63705472696D03 +:108A3D0020696E2072616E676520302E2E333120D5 +:108A4D006F7220686974205B454E5445525D2074E9 +:108A5D006F2072657475726E3A2000456E74657282 +:108A6D00206E6577206672657175656E6379206914 +:108A7D006E206B487A206F7220686974205B454EBA +:108A8D005445525D20746F2072657475726E3A2074 +:108A9D0000506F70756C61746520544E43204545D0 +:108AAD0050524F4D2064617461206279207265616E +:108ABD0064696E67206D6F64656D2072656769739B +:108ACD00746572730031202D20456E61626C656492 +:108ADD002E204C4E41206761696E207365742062B3 +:108AED00792074686520696E7465726E616C2041C1 +:108AFD004743206C6F6F700031202D204F766572CB +:108B0D00777269746520544E4320454550524F4D40 +:108B1D002073657474696E67732077697468206D4E +:108B2D006F64656D206461746100416476616E638C +:108B3D0065642073657474696E6773202873656549 +:108B4D002064617461736865657420666F72206D51 +:108B5D006F726520696E666F293A0030202D2044B2 +:108B6D00697361626C65642E204C4E4120676169AA +:108B7D006E20736574206279207265676973746500 +:108B8D0072204C6E614761696E00546865206465A2 +:108B9D006661756C742076616C7565206973205003 +:108BAD00203D2036332028692E652E2C2070203D47 +:108BBD0020302E3235292E004D6F64656D20737077 +:108BCD0072656164696E6720666163746F72206996 +:108BDD006E206368697073207065722073796D62A1 +:108BED006F6C3A00887B05B7051E06BF061C000496 +:108BFD001F0624020C055F92AF0005885CA30004DC +:108C0D0026F5320007320006320005320004848753 +:108C1D004B49535320544E4320666F7220535831A5 +:108C2D00323778206F72205246284D293978204CE2 +:108C3D006F5261206D6F64656D7300BF0092CE0041 +:108C4D008D0096565D2603BE008791CE0090BF0223 +:108C5D008D0096564F92C70290BE02905C90BF0257 +:108C6D005A27DA20EF205B454E5445525D202D20CA +:108C7D0052657475726E20746F2074686520707201 +:108C8D006576696F7573206D656E7565005B454E14 +:108C9D005445525D202D2052657475726E20746F8F +:108CAD00207468652070726576696F7573206D65C7 +:108CBD006E75650089FEBF001E01EE02BF0285873D +:108CCD0089FEBF041E01EE02BF068587908990BE06 +:108CDD0000FF90BE02EF0290858788895EB6014243 +:108CED00891E03B6004272FB014F02891E05B601B3 +:108CFD004272FB015B0684874D2704574A26FC8789 +:108D0D0034202D2044756D70205358313237782022 +:108D1D0068617264776172652072656769737465E5 +:108D2D0072730031202D204920616E642051207313 +:108D3D0069676E616C732061726520696E7665720C +:108D4D0074656400526567446574656374696F6E1C +:108D5D005468726573686F6C6420283078333729D6 +:108D6D003A2030780043757272656E7420502D7004 +:108D7D006172616D657465722028646563696D61EA +:108D8D006C293A200031202D205472616E73706170 +:108D9D0072656E742052462055415254206272699C +:108DAD006467650033202D20426F6F7374206F6EE2 +:108DBD002C2031353025204C4E4120637572726563 +:108DCD006E74004D6F64656D20636F64696E67200E +:108DDD007261746520696E2073796D626F6C733A80 +:108DED00002020205265674D6F64656D436F6E6680 +:108DFD00696733202830783236293A203078009050 +:108E0D00AE811C2011931C000389938D008F3A8530 +:108E1D00928D0005909390A3813D26E98736202DF4 +:108E2D00204D6F64656D20737072656164696E6746 +:108E3D0020666163746F723A200043757272656EBD +:108E4D007420536C6F7454696D6520286465636973 +:108E5D006D616C293A2000202020526567507265A3 +:108E6D00616D626C654D7362202830783230293A1D +:108E7D0020307800202020526567507265616D6248 +:108E8D006C654C7362202830783231293A20307865 +:108E9D00005265674465746563744F7074696D69DC +:108EAD007A65202830783331293A2030780043759F +:108EBD007272656E742053796E63576F72642028D9 +:108ECD00646563696D616C293A200031202D205451 +:108EDD004E432073657269616C20706F72742073DC +:108EED00706565643A200043757272656E74205426 +:108EFD007844656C61792028646563696D616C29BE +:108F0D003A20004F6E626F6172642053656D746517 +:108F1D00636820636869702069643A20307800883E +:108F2D00F6B701E601B702E602B703848788F6B704 +:108F3D0005E601B706E602B707848738202D204DD8 +:108F4D006F64756C6174696F6E2062616E647769B0 +:108F5D006474683A2000507265737320616E7920D5 +:108F6D006B657920746F20636F6E74696E75650023 +:108F7D0033202D20496E697469616C6973652054C5 +:108F8D004E4320454550524F4D00303031202D3E3F +:108F9D00204731203D206D6178696D756D206761C9 +:108FAD00696E00313130202D3E204736203D206D39 +:108FBD00696E696D756D206761696E0030202D20B9 +:108FCD00544E43206F7065726174696F6E206D6FC2 +:108FDD0064653A20005265675061436F6E6669673C +:108FED00204F7574707574506F7765723A005265C5 +:108FFD006750706D436F7272656374696F6E202870 +:10900D0030783237293A0037202D204D6F64656D49 +:10901D0020636F64696E6720726174653A20003554 +:10902D00202D204C6F7744617461726174654F70AF +:10903D0074696D697A650030202D2044656661750F +:10904D006C74204C4E412063757272656E740043D2 +:10905D00757272656E742050706D436F72726563B8 +:10906D0074696F6E3A200030202D20307830332017 +:10907D002D3E2053463720746F2053463132003039 +:10908D00202D2030783041202D3E2053463720743E +:10909D006F205346313200544E432073657269611F +:1090AD006C20706F72742073706565643A00612076 +:1090BD002D20446574656374696F6E5468726573B1 +:1090CD00686F6C64005265675061436F6E666967C7 +:1090DD00202830783039293A203078005265674998 +:1090ED006E766572744951202830783333293A20D1 +:1090FD00307800AE07FF948D0097BC5D27048D007E +:10910D008E0C8D00B2B3AC0097AC342F352028787F +:10911D0020312E3235206F766572686561642900C5 +:10912D00342F3720287820312E3735206F76657211 +:10913D006865616429004D6F64756C6174696F6E4B +:10914D002062616E6477696474683A0035202D2061 +:10915D004D6F64656D206672657175656E63793AE4 +:10916D0020000D0A4F7065726174696F6E20636F18 +:10917D006D706C65746564000D0A4E6F20616374CB +:10918D00696F6E20706572666F726D6564005265F1 +:10919D00675061436F6E66696720506153656C65FA +:1091AD0063743A005265675061436F6E666967205C +:1091BD004D6178506F7765723A0032202D204164F1 +:1091CD0076616E6365642073657474696E67730090 +:1091DD00342F3620287820312E35206F7665726831 +:1091ED006561642900526567506152616D70202878 +:1091FD0030783041293A203078004C6F77446174D3 +:10920D0061726174654F7074696D697A653A005366 +:10921D00656C65637420544E432073657474696E78 +:10922D0067733A0043757272656E7420667265716C +:10923D0075656E63793A200052656753796E635791 +:10924D006F7264202830783339293A00342F382052 +:10925D0028782032206F7665726865616429003840 +:10926D00202D204465746563744F7074696D697A3F +:10927D0065005265674C6E61204C6E61426F6F7375 +:10928D007448663A0043757272656E74205072654B +:10929D00616D626C653A200031202D204261736949 +:1092AD00632073657474696E67730088BF045F9281 +:1092BD00AF000172D700045C905A26F384873320E7 +:1092CD002D20502D706172616D657465723A2000AC +:1092DD0037202D2050706D436F7272656374696F06 +:1092ED006E0033202D204F7574707574506F776537 +:1092FD00723A20005265674F6370202830783042F3 +:10930D00293A2030780043757272656E74204F6370 +:10931D00705472696D3A20005265674C6E61202859 +:10932D0030783043293A20307800303132333435BB +:10933D0036373839414243444546004D6F64656DBB +:10934D00206672657175656E63793A0030202D2047 +:10935D004F43502064697361626C65640032202D47 +:10936D00204C6E61426F6F73744C663A200033204F +:10937D002D204C6E61426F6F737448663A20004227 +:10938D00617369632073657474696E67733A003134 +:10939D00202D204F435020656E61626C6564005234 +:1093AD0065674C6E61204C6E614761696E3A0031A4 +:1093BD00202D2030783035202D3E2053463600307C +:1093CD00202D206E6F726D616C206D6F64650031A4 +:1093DD00202D2030783043202D3E2053463600304E +:1093ED00303030202D3E20332E34206D7300303040 +:1093FD003131202D3E203530302075730030313025 +:10940D0030202D3E20323530207573003031303113 +:10941D00202D3E2031323520757300303131302012 +:10942D002D3E203130302075730034202D20536CAB +:10943D006F7454696D653A200031202D20506153B1 +:10944D00656C6563743A200032202D204D61785093 +:10945D006F7765723A200031302028313032342058 +:10946D0043505329003131202832303438204350B5 +:10947D0053290031322028343039362043505329B6 +:10948D000030313131202D3E2036322075730031C0 +:10949D00303030202D3E2035302075730031303086 +:1094AD0031202D3E20343020757300313031302085 +:1094BD002D3E2033312075730031303131202D3E5A +:1094CD002032352075730031313030202D3E203261 +:1094DD00302075730031313031202D3E2031352053 +:1094ED0075730031313130202D3E203132207573AE +:1094FD000031313131202D3E203130207573003255 +:10950D00202D20547844656C61793A200036202D49 +:10951D00204167634175746F4F6E0032202D204FCF +:10952D0063705472696D3A20005265674F63702005 +:10953D004F63704F6E3A0031202D204C6E614761A4 +:10954D00696E3A200030303031202D3E2032206DB2 +:10955D00730030303130202D3E2031206D730032BC +:10956D00202D204B49535320544E4300502D7061F4 +:10957D0072616D657465723A0030202D2050614323 +:10958D006F6E6669670034202D20507265616D62C3 +:10959D006C650039202D20496E7665727449710015 +:1095AD0062202D2053796E63576F72640031202D28 +:1095BD002050415F424F4F5354004F435020646938 +:1095CD007361626C65640030202D204469736162A3 +:1095DD006C65640037202831323820435053290000 +:1095ED00382028323536204350532900392028356C +:1095FD0031322043505329004D4F44455F4252496B +:10960D004447450031202D204F63704F6E3A2000A6 +:10961D004F435020656E61626C65640048484EA4EE +:10962D00F08744444EA40F8736202836342043500B +:10963D0053290031202D20506152616D700041671A +:10964D00634175746F4F6E3A0090BE0072A90002AF +:10965D0090BF008733312E3235206B487A00303180 +:10966D0030202D3E20473200303131202D3E204715 +:10967D003300313030202D3E204734003130312041 +:10968D002D3E20473500544E43206D6F64653A00E2 +:10969D004D4F44455F4B49535300536C6F745469A0 +:1096AD006D653A00507265616D626C653A00F6B198 +:1096BD00012604EE01B3028731302E34206B487A37 +:1096CD000031352E36206B487A0032302E38206B23 +:1096DD00487A0034312E37206B487A0036322E35D9 +:1096ED00206B487A002078203130206D730054783B +:1096FD0044656C61793A0050415F424F4F535400BD +:10970D004F63705472696D3A00372E38206B487A6A +:10971D0000313235206B487A00323530206B487A73 +:10972D0000353030206B487A00756E6B6E6F776E3A +:10973D000032202D204F63700033202D204C6E61A0 +:10974D000030202D2052464F00313135323030005F +:10975D003233303430300034363038303000393236 +:10976D00313630300031393230300033383430302A +:10977D000035373630300089858D0097D239363037 +:10978D003000206B487A005B324A00202D200020EB +:10979D0030620052464F00207C2000AC0097BFACD9 +:1097AD000097845B48003A20000D0A000D0A005F07 +:1097BD005C879D20FD20002B00610062006300642A +:1097CD00006500660020FE008D00866352118D003D +:1097DD0087918D00C845968D00C9A94EA40F3F02F3 +:1097ED00B703968D00C69C968D00C6C272BB000A4B +:1097FD00BF0A4F92BD00095B11AC0098B98D008670 +:10980D00D28D00867F8D0087A88D00879BAEFFF7D8 +:10981D00BF065F5ABF048D0087508D0085BD2E1881 +:10982D008D00856E0000000A8D0087678D00875DB5 +:10983D008D00980A8D0087A88D0087508D00856852 +:10984D000000000AA630B0078D00989F8D00875D3F +:10985D008D0086AAAC0087398D0086638D00867FCA +:10986D008D0087918D0087B23D042A08A62D8D00AD +:10987D00989F200C8D0087438D0084528D00876743 +:10988D008D00875D8D00980A8D00C8BE8D0086AA5B +:10989D00201A92BD000D5F5C72BB000EBF0E8702D9 +:1098AD008D0086298D0098BE8D00C92F8D00869B59 +:1098BD00878D0087678D0098C7878D00A0DE878D07 +:1098CD000086343B000A8D00A7A9350000013590B4 +:1098DD00000235A400038D009C94AE0653BF089280 +:1098ED00BC06505F9701A40702905FB60A9097BF20 +:1098FD000090B3002604A62B2002A6208D009B2DE0 +:10990D00A10825DB8D009C8F32000AAC00873E8DAF +:10991D000098CC8F72070646198D009CD9B700A10F +:10992D0008240F92BC0650A4F8BA00B7004F8D0062 +:10993D00C4188D00A99B26068D00A85226D1725FF2 +:10994D00064D873B00088D00A7A93500000135960F +:10995D000002359300038D00A25E92BC0650444474 +:10996D0044A403B7084A2604A62B2002A6208D0086 +:10997D00C90635000001358D0002359200038D00BA +:10998D00C96BA1022604A62B2002A6208D00C906B4 +:10999D003500000135950002356C00038D00C8C7F8 +:1099AD00320008878D0099508F72070646248D006E +:1099BD00AA6D27054A270C201992BC0650A4E7AAC8 +:1099CD0008200892BC0650A4E7AA10B7004F8D00DE +:1099DD00C4188D00A99B26068D00A85226C6725F5D +:1099ED00064D878D0086D28D00867FAE06383F0DE1 +:1099FD00BF0E8D00A7A93500000135930002354833 +:109A0D0000038D00A25E3500000135920002353154 +:109A1D0000038D009F488D0087678D00875D8D0049 +:109A2D0098658D00875D8D009F6835000001358A32 +:109A3D000002356800038D00C49DA60A8D00875D68 +:109A4D008D00BE068D00AAA7201C5F97BF065FBFC5 +:109A5D00048D0086CDAE000ABF068D00847D8D007D +:109A6D0087088D00C6095FB608978D0087848D0025 +:109A7D00C87192BC000526D2BE002602BE02273B4D +:109A8D008D0083178D00813D447A00008D0081DEAD +:109A9D00427424008D0082C78D00878E8D00C472A4 +:109AAD00450900A6058D00C418450A00A6068D00BF +:109ABD00C418450B00A6078D00C4188D00A99B2660 +:109ACD0009C606382704AC0099FF8D0086AAAC00A4 +:109ADD0087398D0086343B000A8D00A7A93500001B +:109AED0001358B000235C500038D009C94AE066BCD +:109AFD00BF08A61E8D00C4468D00C91F5FB60A970C +:109B0D001C0006B3002604A62B2002A6208D009B68 +:109B1D002DA10725DD8D009C8F32000AAC00873EFC +:109B2D008D00C906B60AAB318D00C9068D00A5F2B0 +:109B3D00BE088D008F2C8D00BFA9B60A4CB70ABE8A +:109B4D00081C0003BF08873B00088D009ADF8F7249 +:109B5D0006064604AC009C068D00C9AEA61E8D00FF +:109B6D00C446A40FB708A631C1064D2622B608AAD1 +:109B7D00648D009C1E8D009C89AA018D009C2EAACF +:109B8D00058D00C7048D00C8A2350C00002064A609 +:109B9D00CECB064DA1062461AE064DF6A03227119F +:109BAD004A27144A27174A271A4A271D4A272020D1 +:109BBD0026B608AA74201CB608AA842016B608AAD0 +:109BCD00942010B608AAA4200AB608AAB42004B698 +:109BDD0008AAC48D009C1E8D009C89A4FE8D009C3E +:109BED002EAA038D00C704350A00008D00C8A635C6 +:109BFD000A0000A6148D00C4188D00A99B260A8D9D +:109C0D0000A8522704AC009B57725F064D32000826 +:109C1D00878D00C98BA61E8D00C927A60DAC00C46B +:109C2D00188D009DAFA6318D00C446A4F8878D0018 +:109C3D0086343B000A8D00A7A935000001358D0043 +:109C4D000235D000038D009C94AE0680BF088D00B8 +:109C5D009C89445F9701A40702BF00B60A975CB3C5 +:109C6D00002604A62B2002A6208D009B2DA10425E5 +:109C7D00DD8D009C8F32000AAC00873EA61DAC0026 +:109C8D00C4468D00C6F4878D00A2633F0A873B0052 +:109C9D00088D009C3B8F720706461D8D009CD9A137 +:109CAD000424158D009C898D00A6604888B600A4FB +:109CBD00F1B700848D009DA88D00A99B26068D000F +:109CCD00A85226CD725F064D320008878D00C91748 +:109CDD00A6CFCB064D878D0086343B000A8D00A79D +:109CED00A93500000135910002354300038D009C1C +:109CFD0094AE068CBF08A61D8D00A945B10A260499 +:109D0D00A62B2002A6208D00C906B60AAB308D0009 +:109D1D009B35A10A25E08D009C8F32000AAC00878F +:109D2D003E3B00088D009CE38F72070646598D005F +:109D3D00A64E2453A61D8D00A52E88B600A40FB7E0 +:109D4D0000848D009DA8A639C1064D26263502003A +:109D5D0000A6368D00C53935020000A6168D00C44B +:109D6D0018357F0000A63A8D00C539357F0000A655 +:109D7D0017201035030000A6368D00C539350300B8 +:109D8D0000A6168D00C4188D00A99B26068D00A86F +:109D9D00522691725F064D32000887BA008D009DE4 +:109DAD00AF87B708450800A61D8D00C539450800C9 +:109DBD00A60CAC00C4188D0086638D00A7A93500D4 +:109DCD00000135930002358C00038D00A25E350035 +:109DDD000001358F000235C900038D00C49D92BC72 +:109DED000650444444A4034A27054A2710201C3535 +:109DFD0000000135960002350500032012350000E4 +:109E0D000135960002359D000320048D009F318D94 +:109E1D0000BFA935000001358E000235D800038D35 +:109E2D0000C49D92BC0650A407A108240D8D009F6F +:109E3D007C1C06538D008F2C20048D009F318D00CE +:109E4D00BFA93500000135950002350C00038D00CA +:109E5D009FE68D009F8A350000013592000235CBBB +:109E6D0000038D00C49D8D00A0B635000001359412 +:109E7D000002353700038D00C49D8D00A0968D0026 +:109E8D009F8A3500000135910002355900038D0080 +:109E9D009F488D0098BE8D009F6835000001358E5E +:109EAD000002352A00038D00C49DA61E8D00A94514 +:109EBD00ABFAA107240D8D009F7C1C066B8D008FC6 +:109ECD002C20048D009F318D00BFA9350000013578 +:109EDD00900002351400038D009F3E44A4074AA153 +:109EED0004240D8D009F7C1C06808D008F2C20047A +:109EFD008D009F318D00BFA935000001358F000207 +:109F0D00354800038D009F3E4EA40FA10A240D8DF0 +:109F1D00009F7C1C068C8D008F2C20048D009F31A2 +:109F2D00AC0098B5350000013597000235360003B9 +:109F3D00878D00C49DA61DAC00C4468D00C49D8DAB +:109F4D0000C1868D0083178D00813D427424008DE4 +:109F5D000081DE447A0000AC0082C78D00C49D35BF +:109F6D0000000135970002358F0003AC00BFA990AA +:109F7D005F9097AE0003BF0093AC008CE78D00A0FF +:109F8D00C98D00A0D28D00C49D350000013596000D +:109F9D000235F20003AC00BFA98D0086638D00A7CA +:109FAD00A9350000013596000235FB00038D00A296 +:109FBD005E35000001358E000235F400038D009FE3 +:109FCD00E68D00A0BD350000013589000235F70092 +:109FDD00038D00C49DAC0098B98D00A0AA8D00A082 +:109FED009EAE0002878D0086638D00A7A935000007 +:109FFD000135950002357900038D00BFA9350000AC +:10A00D00013587000235BC00038D00BFA935000066 +:10A01D0001358B0002359700038D00A25E350000DF +:10A02D0001358D0002357200038D00A0A58D00A0B5 +:10A03D00B63500000135890002350A00038D00C4D4 +:10A04D009DAC0098B98D0086638D00A7A9350000E1 +:10A05D00013596000235A700038D00A25E35000084 +:10A06D0001358E0002354700038D00A0A58D00A09F +:10A07D00968D00A0BD3500000135890002358300A5 +:10A08D00038D00C49DAC0098B98D00A09EAE000458 +:10A09D0087AE0650AC008F2C8D00A0AA878D00C412 +:10A0AD009DAE06383F09BF0A878D00A09EAE000306 +:10A0BD008D00A0C98D00A0D2AC00BFA98D00C83AFB +:10A0CD0092BC0001875F97BF065FBF048D00A0DEC5 +:10A0DD00878D0087538D009865AC0087538D008662 +:10A0ED00638D00C9B38D009DC38F7206064604AC07 +:10A0FD0000A1998D00C972271A4A271D4A27204AA7 +:10A10D00273A4A27544A276E4A27714A27744A2705 +:10A11D007720798D0099B120738D00991C206D8D5C +:10A12D00009FA68D00AA8827638D00A1B1A6028D80 +:10A13D0000A9972657C6063826E520508D009FF2B8 +:10A14D008D00AA8827468D00A1B1A6038D00A99781 +:10A15D00263AC6063826E520338D00A0528D00AA7A +:10A16D008827298D00A1B1A6048D00A997261DC6AB +:10A17D00063826E520168D0099F020108D009B5491 +:10A18D00200A8D009C9B20048D009D2E8D00A99B87 +:10A19D00260A8D00A8522704AC00A0F2725F064D6E +:10A1AD00AC0098B98D0087538D00C55A8D00876717 +:10A1BD00450700878D0086343F093F088D00A7A90C +:10A1CD0035000001358A0002359E00038D00BFA9C0 +:10A1DD003500000135880002350600038D00A25EB2 +:10A1ED0035000001358B0002350500038D00C92FA8 +:10A1FD003D0927243F093D08270E35000001359103 +:10A20D000002356F0003200C35000001359100026E +:10A21D00358500038D00BFA98F720706461B3501DA +:10A22D0000098D00C9AEA631C1064D260A8D00B3B9 +:10A23D00A03501000820023F088D00A99B260A8D3C +:10A24D0000A8522704AC00A1C9725F064DAC00876F +:10A25D003E8D00A263878D00BFA98D00C6E4878D5A +:10A26D0000A7A935000001358B0002353700038D9D +:10A27D0000A25E3500000135950002358600038D84 +:10A28D0000BFA93500000135960002354000038D51 +:10A29D0000BFA93500000135970002353E00038D42 +:10A2AD0000BFA93500000135970002354600038D2A +:10A2BD0000BFA93500000135950002359300038DCF +:10A2CD0000BFA93500000135900002352C00038D2B +:10A2DD0000BFA93500000135950002351A00038D28 +:10A2ED0000BFA9350000013592000235DD00038D58 +:10A2FD0000BFA93500000135920002356C00038DB9 +:10A30D0000BFA9350000013595000235A000038D71 +:10A31D0000BFA9350000013590000235BB00038D4B +:10A32D0000BFA9350000013595000235AD00038D44 +:10A33D0000A344AC00BFA98D00BFA98D00A34D871C +:10A34D0035000001358C0002359A0003878D00869B +:10A35D00638D00A7A9350000013590000235D200AC +:10A36D00038D00C49DA6098D00A8C6A6098D00C841 +:10A37D00D03500000135940002354600038D00A351 +:10A38D00E5A580270E350000013597000235040044 +:10A39D0003200C350000013597000235A000038D18 +:10A3AD0000BFA93500000135940002355500038D1D +:10A3BD0000A3E54EA40F5F9701A4078D00AA963563 +:10A3CD000000013592000235EF00038D00A3E55F1B +:10A3DD009701A40FAC0098AC8D00C49D8D00A4E82E +:10A3ED00873B00088D00A7A93500000135910002BB +:10A3FD00359B00038D00A4DF484F49B70827068D14 +:10A40D0000C7E320048D00C7D68D00C49D35000024 +:10A41D000135970002354E00038D00BFA93D082779 +:10A42D00068D00C7D620048D00C7E38D00C49D3571 +:10A43D000000013595000235BA00038D00C92F3299 +:10A44D000008873B00088D00A3EE8F720706461D9E +:10A45D008D00C97227054A270A20128D00A4E8A491 +:10A46D007F20068D00A4E8AA808D00A53D8D00A952 +:10A47D009B26068D00A85226CD725F064D32000830 +:10A48D00878D0086D23B000D8D00A7A935000001F8 +:10A49D003591000235B100038D00A4DF4EA40FA449 +:10A4AD00078D00A6028D00A7E0B10826068D00C716 +:10A4BD00D620048D00C7E38D00C49D8D00C9378D56 +:10A4CD0000A5E0A10825DE8D00A5D832000DAC0059 +:10A4DD0087398D00A2638D00A4E887A609AC00C45E +:10A4ED00463B00088D00A48E8F720706461F8D0017 +:10A4FD00C9AEA6D0CB064DA1082412A6098D00A584 +:10A50D002E88B600A48FB700848D00A53B8D00A9C1 +:10A51D009B26068D00A85226CB725F064D32000891 +:10A52D00878D00C446B700C6064D4EA4F087BA000D +:10A53D008D00C98BA6098D00C927A608AC00C418CB +:10A54D008D0086D23B000D8D00A7A9350000013589 +:10A55D008F000235E200038D00A4DFA40F8D00A64D +:10A56D0002B60DB10826068D00C7D620048D00C792 +:10A57D00E38D00A7D524068D0087532037A00A2729 +:10A58D00114A27144A27174A271A4A271D4A2720F6 +:10A59D0020268D00A79C201C8D00A78F20168D00D6 +:10A5AD00A78220108D00A775200A8D00A7682004B2 +:10A5BD008D00A75B8D00C49D8D00A5E0A11025A188 +:10A5CD008D00A5D832000DAC0087398D00A34DACA0 +:10A5DD0000BFA98D00A5F28D0087538D00BFA9B6D0 +:10A5ED000D4CB70D873500000135970002359800E9 +:10A5FD0003AC00C49DB7083F0D8D00A0AE873B0096 +:10A60D00088D00A54D8F72070646228D00A64E259A +:10A61D0009A69FCB064DA1062413A6098D00A65AA7 +:10A62D00A10A2505A6A9CB064D8D00A53B8D00A938 +:10A63D009B26068D00A85226C8725F064D32000873 +:10A64D00878D00C917A6D0CB064DA10A878D00C4F2 +:10A65D0046A4F0B700A6D0CB064D878D00A35A8F28 +:10A66D00720706461E8D00AA6D27084A270B4A273A +:10A67D000E20108D00A450200A8D00A4EE20048D14 +:10A68D0000A60B8D00A99B26068D00A85226CC7224 +:10A69D005F064D878D0086D28D00867F8D00A7A920 +:10A6AD00350000013591000235F200038D00C49D87 +:10A6BD00A60A8D00A8C6A60A8D00C8D0A60A8D00D0 +:10A6CD00C446A40FB7083F0DAE06AABF0EB60DB116 +:10A6DD000826068D00C7D620048D00C7E38D00A780 +:10A6ED00D524068D0087532037A00A27114A271439 +:10A6FD004A27174A271A4A271D4A272020268D0048 +:10A70D00A79C201C8D00A78F20168D00A7822010DE +:10A71D008D00A775200A8D00A76820048D00A75B0A +:10A72D008D00C49D8D00A5F2BE0E8D008F2C8D0069 +:10A73D00BFA9B60D4CB70DBE0E1C0003BF0EA11068 +:10A74D00258B8D00A5D88D0086AAAC0087393500E4 +:10A75D0000013597000235D0000387350000013523 +:10A76D0097000235CE0003873500000135970002B2 +:10A77D0035CC000387350000013597000235CA003E +:10A78D000387350000013597000235C80003873572 +:10A79D000000013597000235C6000387A61B8D000A +:10A7AD00C9063500000135970002359400038D0070 +:10A7BD00C49DA61B8D00C90635000001359700020A +:10A7CD0035B00003AC00C49D8D00C49D8D00A7E085 +:10A7DD00A10A875FB60D97BF025FBF008D0084523F +:10A7ED008D0087678D0087538D00980A4F92BD00AD +:10A7FD0001B60D873B00088D00A6A18F7207064696 +:10A80D00308D00A64E2509A69FCB064DA10624210D +:10A81D00A60A8D00A65AA10A2505A6A9CB064DBAF2 +:10A82D00008D00C98BA60A8D00C9B8A6098D00C47C +:10A83D00188D00A99B26068D00A85226BA725F06B8 +:10A84D004D32000887A60DC1064D878D0086638D9C +:10A85D0000A7A93500000135930002350100038DD5 +:10A86D0000C49DA60B8D00A8C6A60B8D00C8D035C3 +:10A87D0000000135960002351100038D00C49D8D39 +:10A88D0000A991A520270E3500000135960002354F +:10A89D001D0003200C350000013595000235C70061 +:10A8AD00038D00BFA9350000013595000235280044 +:10A8BD00038D00AA74AC0098AC8D00C4468D00A029 +:10A8CD00AE8D0087538D0097D58D00C9378D00C78C +:10A8DD0024873B00088D00A7A935000001359500A0 +:10A8ED0002353600038D00A25EA60B8D00A94544EE +:10A8FD00B708A50127068D00C7E320048D00C7D634 +:10A90D008D00C49D350000013593000235590003BB +:10A91D008D00C8E227068D00C7D620048D00C7E341 +:10A92D008D00C49D3500000135930002359C000358 +:10A93D008D00C92F320008878D00C4464EA40F87A5 +:10A94D003B00088D00A8DF8F72070646238D00C9D6 +:10A95D007227054A270A20188D00A991A4DF200629 +:10A96D008D00A991AA20B7088D00AA5B8D00AA645D +:10A97D008D00A99B26068D00A85226C7725F064D35 +:10A98D0032000887A60BAC00C4468D00C418A6C0C3 +:10A99D00C50646878D0086D28D0086D752018D0063 +:10A9AD00C9B38D00A8588F7206064604AC00AA41A3 +:10A9BD008D00AA6D27054A270820798D00A94D2005 +:10A9CD00738D00A7A93500000135970002350D00E4 +:10A9DD00038D00A25E350000013593000235130092 +:10A9ED00038D00AA748D00AA9635000001358A00EA +:10A9FD0002353000038D00AA8427398D00A9916B93 +:10AA0D00018D00AAA720048D00C5D48D00C689260E +:10AA1D00F6B603A41F887B02A4E0B70184BA01B780 +:10AA2D00088D00AA648D00AA5B8D00A99B2605C622 +:10AA3D000638268D8D00A99B260A8D00A852270465 +:10AA4D00AC00A9AF725F064D5B01AC0087354508C0 +:10AA5D0000A60BAC00C539450800A60AAC00C41809 +:10AA6D008D00C917A031878D00C49DA60B8D00C424 +:10AA7D00465F9701A41F878D00C49DA60A8D008790 +:10AA8D00538D00BE06C6063887028D0086298D00BF +:10AA9D0087678D0098C7AC00BFA95FBF02BF003F9D +:10AAAD0008878D0086638D00A7A9350000013593B9 +:10AABD000002352500038D00C49DA60C8D00A8C68F +:10AACD00A60C8D00C8D03500000135950002354427 +:10AADD0000038D00C8EB4EA40F448D00A0C13500BE +:10AAED00000135930002356A00038D00C8EB444424 +:10AAFD00445F9701A4038D00AA963500000135939C +:10AB0D000002357B00038D00C8EB5F9701A403ACF9 +:10AB1D000098AC8D0086D78D00866335FF000DA69D +:10AB2D0030C506462604AC00AC2AC6064E2704AC3A +:10AB3D0000AC5F72190646720B064620721B064664 +:10AB4D00A6128D00C446A5502704AC00AC5FA61814 +:10AB5D008D00C446A5012704AC00AC5F5506470027 +:10AB6D00008D00C9562404AC00AC21CE00105CCF82 +:10AB7D000010CE06495ABF0EB600A540270435FE7B +:10AB8D00000D5FB60D97C30649B600240CAA80C709 +:10AB9D000647B60D4AB70D2005A47FC70647A61078 +:10ABAD008D00C94FAE04143F09BF0A2011B600A491 +:10ABBD00EFC7064735020000B60D8D00AC7A5FB6C3 +:10ABCD000D97B30E24193F0CBE0E72B0000CBF0EC4 +:10ABDD00C60647B700A51026D43501000020D93F81 +:10ABED0000B60F8D00AC7A8D00C8AC8D00C5B735A1 +:10ABFD00080000A6128D00C539720D0647048D00A0 +:10AC0D00C898A6058D00C675A6018D00C446A18500 +:10AC1D00274020F4B600A4F7C7064720358D00C5A0 +:10AC2D007AB7008D00AC73AE00038D00AC68B10037 +:10AC3D00250D721806468D00AC73AE0002200B7206 +:10AC4D001A06468D00AC73AE00048D00AC688D0005 +:10AC5D00AF298D00869B8D00871E8772BB0002BFBA +:10AC6D000292BC000187AE0650AC008F2C8D00B84F +:10AC7D00F38D008753AC00BCA2CE001002A40301DB +:10AC8D001C0014F6270B4A270C4A27184A272420A4 +:10AC9D0038AC00AB208D00C956262955041500008F +:10ACAD00A602201C8D00C956261A5504150000A6B3 +:10ACBD0003200D8D00C956260B5504150000A60462 +:10ACCD008D00C4188D00C8AC878D00C8ACCE064968 +:10ACDD0072BB0010CF0010878D0086D28D0086D7F5 +:10ACED008D00C9B38D00A26C8F72070646048D00CE +:10ACFD00C9AEAE064DF6A030273A4A273F4A274443 +:10AD0D004A27494A274E4A27534A27584A2604AC10 +:10AD1D0000ADA44A2604AC00ADEE4A2604AC00AD4D +:10AD2D00F4A0282604AC00ADFA4A2604AC00AE000F +:10AD3D00AC00AE698D00A668AC00AE698D00A801AF +:10AD4D00AC00AE698D00A9A1AC00AE698D00C382C7 +:10AD5D00AC00AE698D00BE72AC00AE698D00C293C1 +:10AD6D00AC00AE698D00C2D1AC00AE698D00C5D40A +:10AD7D008D00C68926F6450308450800A6278D00D7 +:10AD8D00C9B8A6118D00A9972704AC00AE69C606F7 +:10AD9D00382604AC00AE698D00A7A9350000013539 +:10ADAD008F000235FB00038D00A25E3500000135DA +:10ADBD00900002355C00038D00C49DA6278D00C454 +:10ADCD00468D00A0C13500000135880002354A00CE +:10ADDD00038D00AA842604AC00AE698D00AAA720BD +:10ADED008F8D00C20F20758D00C0A3206F8D00C206 +:10ADFD005120698D00A7A93500000135920002355B +:10AE0D004500038D00A25E35000001358E00023530 +:10AE1D00BB00038D00C49DA6398D00C4468D00A0D6 +:10AE2D00C1350000013589000235BD00038D00AA32 +:10AE3D008427298D00AAA720048D00C5D48D00C6B6 +:10AE4D008926F6450308450800A6398D00C9B8A620 +:10AE5D00158D00A9972605C6063826978D00A99B46 +:10AE6D00260A8D00A8522704AC00ACF1725F064D86 +:10AE7D00AC008735720250C434721250C4721F5226 +:10AE8D00B0721152B6353952C1353A52C235FF52F0 +:10AE9D00C335F952C4725F52BF350152C07214529C +:10AEAD00B0721052B5721052B8721052B0A6FDC4E5 +:10AEBD000648C7064887721152B0721152B5721109 +:10AECD0052B6721350C487721152B0725F52BF35B1 +:10AEDD000152C0721052B0A6FDC40648C70648877D +:10AEED00721050C3721F5250721152563506525E77 +:10AEFD003509525F35C45260721052557210525856 +:10AF0D00721052508772115250721150C3874D27D3 +:10AF1D000A8D00AF2E8FC6064E26FA878D00AF2EF6 +:10AF2D008772115250725F525C3501525DC7064EE9 +:10AF3D007210525087721250C3721F52807211528A +:10AF4D00863507528E35F4528F3524529072145235 +:10AF5D0080721052857210528872105280A604CAE7 +:10AF6D000648C706488772115280721152867211B7 +:10AF7D005285721350C38720018F72040648FA87D9 +:10AF8D00720306480D8D00AF428D00AF848D00AF6A +:10AF9D007387AC00AE818D0086D28D0086D752139B +:10AFAD008D00A7A94F6B018D00C9B3AE00028D00B6 +:10AFBD0087B272BB000EBF0E4F6B027B014848B7C4 +:10AFCD00088D00C9C2961C00038D00C9BDB6088D41 +:10AFDD0000C91F968D00C791B6088D00C8B5968D16 +:10AFED0000C5F0350000013597000235B300038D23 +:10AFFD0000C49DB6088D00C446B70C8D00C9C2961D +:10B00D001C00038D00C9BD8D00C91F968D00C79111 +:10B01D00B60C8D00C8B5968D00C5F0A6208D00C963 +:10B02D00068D008777350700008D0087465F5C8DA4 +:10B03D0000C8715FB60C97B6008D008D059FA501F8 +:10B04D002704A6312002A63092BD0001B600B7013B +:10B05D004AB7003D0126D24F92BD00058D00C9377C +:10B06D00350000013597000235A400038D00C49D05 +:10B07D007B024C6B02B6084CB7087B02A10424047A +:10B08D00AC00AFCE8D00C7447B014C6B01A11024E9 +:10B09D0004AC00AFC58D00C74435000001358F00ED +:10B0AD0002356300038D00C49D8F5B13AC008735A3 +:10B0BD00A6018D00C6758D00C82FAE00058D00C987 +:10B0CD0064A6068D00C82BAE00068D00C964A607C8 +:10B0DD008D00C82BAE00078D00C964A6088D00C871 +:10B0ED002BAE00088D00C964A6098D00C82BAE00DB +:10B0FD00098D00C964A60A8D00C82BAE000A8D000B +:10B10D00C964A60B8D00C82BAE000B8D00C964A6BB +:10B11D000C8D00C82BAE000C8D00C964A61D8D00D2 +:10B12D00C82BAE000D8D00C964A61E8D00C82BAEB8 +:10B13D00000E8D00C964A6268D00C82BAE000F8DA4 +:10B14D0000C964A6208D00C82BAE00108D00C96407 +:10B15D00A6218D00C82BAE00118D00C964A6278DC8 +:10B16D0000C82BAE00128D00C964A6318D00C82B0E +:10B17D00AE00138D00C964A6338D00C82BAE00142C +:10B18D008D00C964A6378D00C82BAE00168D00C981 +:10B19D0064A6368D00C539A6368D00C446A102269B +:10B1AD00118D00C82FAE00178D00C964A63A8D0011 +:10B1BD00C539878D0086343B000A8D00AE818D0028 +:10B1CD00AEED5FCF0012CF00108D00C51792BC06FB +:10B1DD0050A4078D00B633A601C40648A8014490BB +:10B1ED0011500FA6018D00C67535FF0000A6128DFA +:10B1FD0000C5393F00A60F8D00C539A61D8D00C4B1 +:10B20D0046A501270EA640CA0647C706478D00C8AA +:10B21D008E2008A6BFC40647C70647A6058D00C6E3 +:10B22D0075A6018D00C446A18526F68D00C59A8DA3 +:10B23D0000C979A54027248D00AED48D00BD1CB763 +:10B24D000A2718AE0514BF0892C6088D00C906BEA0 +:10B25D00085CBF08B60A4AB70A26EDCE061672B0CC +:10B26D000614270F720006460A8D00C4C88D00C350 +:10B27D00E920E8720106461A8D00AED48D00C97919 +:10B28D00A550260EA6188D00C446A50126048D00D6 +:10B29D00BBA28D00AF8DA6C0C40646A1402790323B +:10B2AD00000AAC00873E8D00B488C60648720D506A +:10B2BD000B04AA012002A4FEC706487218500F8D78 +:10B2CD0000C64C8D00AEED8D00C7738D00B0BDA6D0 +:10B2DD00058D00C6758D00C97F8D00C82F5F5C8DF3 +:10B2ED0000C8155F97BF025FBF00A6108D008607CF +:10B2FD00AE06428D008CD9A61B8D00C4465F974FBC +:10B30D00028D0086298D00C820A62C8D00C4465FB5 +:10B31D0097BF025FBF008D00C8204F8D00C675AE70 +:10B32D0006428D008CC1BE002602BE02260C5FCFE8 +:10B33D000642AE0007CF064420043D032710AE069B +:10B34D00428D008CCD450700A6018D00C418A63F87 +:10B35D00C40646B700720B500B0F92BC0650444406 +:10B36D0044A4038D009629BA00C7064620048D001B +:10B37D00C054C606468D00962F4D27F24A27054A22 +:10B38D002708200C8D00B1C020E88D00B56220E2A9 +:10B39D008F20DF8D00C773A6068D00C95DA6058DB4 +:10B3AD0000C418A6078D00C95DA6068D00C418A699 +:10B3BD00088D00C95DA6078D00C418A6098D00C9AA +:10B3CD005DA6088D00C418A60A8D00C95DA6098D5D +:10B3DD0000C418A60B8D00C95DA60A8D00C418A661 +:10B3ED000C8D00C95DA60B8D00C418A61D8D00C95E +:10B3FD005DA60C8D00C418A61E8D00C95DA60D8D11 +:10B40D0000C418A6268D00C95DA60E8D00C418A611 +:10B41D00208D00C95DA60F8D00C418A6218D00C911 +:10B42D005DA6108D00C418A6278D00C95DA6118DCF +:10B43D0000C418A6318D00C95DA6128D00C418A6D2 +:10B44D00338D00C95DA6138D00C418A6378D00C9B4 +:10B45D005DA6148D00C418A6398D00C95DA6158D85 +:10B46D0000C418A6368D00C95DA6168D00C418A699 +:10B47D003A8D00C95DA617AC00C418725F50C03577 +:10B48D00FF500235FF5003725F500035FF500735F6 +:10B49D00FF5008725F500535FF500C35FF500D728F +:10B4AD005F500A35FF501135FF5012725F500F7209 +:10B4BD00135002721250037218500772185008720E +:10B4CD00185005721A5007721A5008721A500972E4 +:10B4DD001B5005721C5007721C5008721C500972CB +:10B4ED001D5005721F5007721E50087214500772BE +:10B4FD001450087216500C7216500D7219500C72B1 +:10B50D0019500D721B500C721B500D721A500EA655 +:10B51D000CCA50A1C750A1721D500C721D500D7256 +:10B52D001C500EA630CA50A1C750A17210501172F6 +:10B53D001050127212501172125012721450117268 +:10B54D001450127216501172165012721850117248 +:10B55D001850129A873B0008A6CFC40646C7064668 +:10B56D00A6DF8D00C8AE8D00AE818D00AEED5FCF34 +:10B57D000012CF00108D00C51792BC0650A4078D88 +:10B58D0000B633A601C40648A801449011500FA679 +:10B59D00018D00C67535FF0000A6128D00C5393F1F +:10B5AD0000A60F8D00C539A61D8D00C446A5012727 +:10B5BD000CA6408D00C94F8D00C8982006A6BF8DE2 +:10B5CD0000C8AEA6058D00C675A6018D00C446A1A6 +:10B5DD008526F68D00C5B7A6128D00C446A5402759 +:10B5ED00128D00BD1C8D00B86220088D00C4C88D61 +:10B5FD0000BA0CCE0616550647000072B006142789 +:10B60D0006B600A50827E4B600A50827088D00AEEC +:10B61D00D48D00AC868D00AF8DA6C0C40646A1802A +:10B62D0027B532000887B700721750027217500302 +:10B63D007217500472145002721450037214500495 +:10B64D0072145000721A50C3A6CFC4509EAA10C7D0 +:10B65D00509E72195234B6004A27144A271B4A27A6 +:10B66D00224A27294A27304A27374A273E2046AE05 +:10B67D007555BF02AE44502044AE9555BF02AE4342 +:10B68D00D0203AAE238EBF02AE438B2030AE638EF8 +:10B69D00BF02AE430B2026AEE38EBF02AE428B201F +:10B6AD001CAEE38EBF02AE420C2012AEE38EBF0283 +:10B6BD00AE418E2008AE6555BF02AE44D0BF008DA1 +:10B6CD0000829290938D0082929FA40F88909EA4E9 +:10B6DD00F0B70184BB01C7523393575757579FC7D4 +:10B6ED0052327216523572145235721A52358752C1 +:10B6FD00118D00A7A935000001358C0002351D0004 +:10B70D00038D00A25E35000001358F00023510005B +:10B71D00038D00C49DA6428D00C841968D00C9A918 +:10B72D00AE06383F01BF024EA40F3F04B705965C2D +:10B73D0072BB0004F692BD00018D00C6A6965C900A +:10B74D00BF0472BB0004F65F5C8D00C86D92BD0036 +:10B75D0005AE00028D00C86D4F92BD00058D00A293 +:10B76D005E3500000135920002351C00038D00A2EC +:10B77D005E350000013592000235A500038D00BF36 +:10B78D00A9350000013591000235C700038D00BFBA +:10B79D00A935000001358F0002357D00038D00BFF6 +:10B7AD00A935000001358D0002350D00038D00BF58 +:10B7BD00A95B11878D0086D28D00A7A935000001E8 +:10B7CD003596000235B100038D00BFA9350000018B +:10B7DD00358E0002356400038D00C49DA6208D00BA +:10B7ED00A8C6A6208D00C4468D00C71435000001E3 +:10B7FD00358E0002358100038D00C49DA6218D007C +:10B80D00C4468D00A8CEA6218D00C8D035000001FC +:10B81D0035920002359200038D00C49DA6208D0047 +:10B82D00C446B708A6218D00C446B7015FB6089778 +:10B83D004FBA0102BF065FBF048D0098C28D00BFD5 +:10B84D00A93500000135890002354700038D00C47C +:10B85D009DAC0087398D0086D2B70A3F0BAE05141B +:10B86D00F6A102251C720B064706A6C08D00C9065F +:10B87D00A6C08D00C9064F8D00C906A6208D00C932 +:10B88D004FAE0514F6A10324043501000BB60BB120 +:10B89D000A243A5F971C0514BF08B60AB00BB70A05 +:10B8AD0092C608A1C0270EA1DB2612A6DB8D00C90A +:10B8BD0006A6DD2008A6DB8D00C906A6DC8D00C915 +:10B8CD0006BE085CBF08B60A4AB70A26D3C60514D9 +:10B8DD002704A103250CA6C08D00C906A6DF8D0087 +:10B8ED00C8AEAC008739B7043F03C60647B702A5FB +:10B8FD0040270435010003B602A480B7012705B621 +:10B90D00034CB703B602A5402705B604C704145F60 +:10B91D00B603973D012709B6001C0413F71D041348 +:10B92D00450302C60648A401A801B705905FB604F9 +:10B93D009097BF0072B9000090BF0620298D00C8F6 +:10B94D00782407B605449011500FCE001090935CEB +:10B95D00CF00109302A403011C0014F6BE00D704FF +:10B96D0014B6024CB702450201BE00B3062FCEB687 +:10B97D0003BB04878D0086D252118D00A7A9350017 +:10B98D000001358E0002359E00038D00C49D8D0093 +:10B99D00C99D8D00C845968D00C754968D00C69CD7 +:10B9AD00968D00C6378D00C99D8D00C8D48D00C9F8 +:10B9BD009DA407B708A10326068D00C7D620048DC8 +:10B9CD0000C7E38D00C49D350000013590000235A0 +:10B9DD007400038D00C96BA10526068D00C7D62006 +:10B9ED00048D00C7E38D00C49D3500000135930023 +:10B9FD000235BC00038D00C92F5B11AC008739B730 +:10BA0D0001A1C02706A1DB2725202F8D00C90F27F7 +:10BA1D0013A6F88D00C8AEA6088D00C94FCE064BF3 +:10BA2D00CF0649875FCF064BB600AA0120558D0082 +:10BA3D00C90F2752B600AA042049720106470CA669 +:10BA4D00FE8D00C8AEA6028D00C94F8D00C90F270F +:10BA5D0035B600A5042604B6012018B600A4FBB720 +:10BA6D0000C70647B601A0DC27074A2612A6DB2031 +:10BA7D0002A6C08D00C3E9CE064B5CCF064B87B640 +:10BA8D0000A4F0C70647878D0086D252118D00A7FE +:10BA9D00A9350000013590000235E900038D00C481 +:10BAAD009DA6338D00C841968D00C754968D00C656 +:10BABD009C968D00C637A6338D00C8D0A6338D0059 +:10BACD00C446A440B70827068D00C7E320048D00A7 +:10BADD00C7D68D00C49D350000013593000235CCCD +:10BAED0000038D00BFA93D0827068D00C7D6200491 +:10BAFD008D00C7E38D00C49D35000001358D00021A +:10BB0D00353000038D00C92F5B11AC0087398D00D6 +:10BB1D0086D252118D00A7A935000001358D000286 +:10BB2D00355100038D00C49DA6378D00C841968DFB +:10BB3D0000C754968D00C69C968D00C637A6378DCE +:10BB4D0000C8D0A6378D00C446B708A10A26068DB9 +:10BB5D0000C7D620048D00C7E38D00C49D350000BD +:10BB6D000135900002358C00038D00C96BA10C26A8 +:10BB7D00068D00C7D620048D00C7E38D00C49D350A +:10BB8D000000013593000235DC00038D00C92F5BE9 +:10BB9D0011AC0087398D0086343B000A35FF000A51 +:10BBAD00CE001272B00010BF08720D06470435FEAC +:10BBBD00000A275B5FB60A97B3082418BF00BE08BA +:10BBCD0072B00000BF088D00C7648D00C979A5084B +:10BBDD0026E220F6BE0827103F00B6098D00C76487 +:10BBED008D00C979A50827F88D00C59A3508000084 +:10BBFD00A6128D00C539720D0647048D00C88EA69C +:10BC0D00058D00C675A6018D00C446A18526F67268 +:10BC1D0011064632000AAC00873E8D0086D25211C5 +:10BC2D008D00A7A93500000135960002354B0003A4 +:10BC3D008D00C6208D00C841968D00C754968D008D +:10BC4D00C69C968D00C6378D00C6AFB708A50127D7 +:10BC5D00068D00C7E320048D00C7D68D00C49D3529 +:10BC6D00000001358B0002356800038D00C8E22706 +:10BC7D00068D00C7D620048D00C7E38D00C49D3509 +:10BC8D00000001358A000235D200038D00C92F5BFB +:10BC9D0011AC0087398D0086D28D008791B7088D44 +:10BCAD0000C8FD35080000A6128D00C5393F00A65D +:10BCBD000E8D00C5398D00C93F720D06470635FF43 +:10BCCD0000002003450800A6228D00C53945080057 +:10BCDD008D0087534F8D00C13E7215500572155062 +:10BCED000F7217500F7214500F7217500F721250AF +:10BCFD000F7216500A8D00C9CC350300008D00C996 +:10BD0D00A3A6128D00C446A50827F6AC0087398D71 +:10BD1D000086D23F0BA6128D00C446A52026428D6B +:10BD2D0000C8FDA6108D00C446B7008D00C9417234 +:10BD3D000D0647034F2002A6138D00C446B70B27EF +:10BD4D001CAE0514BF08450B0A4F8D00C44692C7A3 +:10BD5D0008BE085CBF08B60A4AB70A26EC8D00C9B2 +:10BD6D003F35700000A6128D00C5398D00C9CC3548 +:10BD7D000500008D00C9A3A6018D00C446A185262E +:10BD8D00F6B60BAC0087393B00088D00C0F18F7201 +:10BD9D00070646518D009CD9A10624498D00C991F5 +:10BDAD00A41FB708AE064DF6A03127114A27144A35 +:10BDBD0027174A271A4A271D4A27202024B608AAE2 +:10BDCD0020201CB608AA402016B608AA602010B67E +:10BDDD0008AA80200AB608AAA02004B608AAC0B7EF +:10BDED00088D00C7828D00A99B26068D00A85226BE +:10BDFD0099725F064D320008878D0086638D00872E +:10BE0D006AB7023F008F720706462C721706465519 +:10BE1D00064F0001720F5230FBB601C752315FB6AB +:10BE2D0000978D00879E72BB000ABF0AB60192BDB6 +:10BE3D000009B6004CB700B601A10D2706B600B13A +:10BE4D000225C2B600B1025F2502B6029772BB0091 +:10BE5D0006BF065F5A72BB0006BF064F92BD0005B6 +:10BE6D008D00869B878D0086D28D0086D752028DE0 +:10BE7D0000C9B38D00B7C18D00AA88274B8D00AACC +:10BE8D00A720048D00C5D48D00C68926F6BE021FDD +:10BE9D0001965C3F018D00C862A6208D00C9B8A631 +:10BEAD000F8D00C418965C3F01BF025F5C72BB0032 +:10BEBD00028D00C862A6218D00C9B8A6108D00A9FB +:10BECD00972605C6063826AB5B02AC0087358D007C +:10BEDD0086D28D00A7A935000001359200023507E5 +:10BEED0000038D00C6208D00A8C68D00C6AF44B7D7 +:10BEFD0008A50127068D00C7E320048D00C7D68D48 +:10BF0D0000C49D350000013595000235D400038D28 +:10BF1D0000C8E227068D00C7D620048D00C7E38D2B +:10BF2D0000C49D350000013588000235CC00038D1D +:10BF3D0000C92FAC0087393B00088D00A7A935003B +:10BF4D00000135920002357F00038D00A25E8D0049 +:10BF5D00C991A403B70826068D00C7D620048D000D +:10BF6D00C7E38D00C49D35000001359000023544B6 +:10BF7D0000038D00C96BA10326068D00C7D62004D2 +:10BF8D008D00C7E38D00C49D35000001358D000285 +:10BF9D0035B100038D00C8C7320008878D00876A50 +:10BFAD002009720F5230FB8D00C8093F015FBF029F +:10BFBD00BE06893B0005965C8D0096BB84852706E1 +:10BFCD0092BC000526DC350000053597000635B915 +:10BFDD0000072009720F5230FB8D00C809BE06897B +:10BFED003B0005965C8D0096BB8485270692BC00B0 +:10BFFD000526E1873B0000720B5230407216064653 +:10C00D00555231064FCE061672B00614A3001625F2 +:10C01D000F5506480000B600A40134009011500FD2 +:10C02D0090CE0616935CCF061651024F02A41F0246 +:10C03D005172A90618C6064F90F72003C6523172E9 +:10C04D0012064632000080A6018D00C6758D00AF28 +:10C05D00128D00C5174F8D00B633725F064D8D00E2 +:10C06D00B6FC8F72070646278D00AA6D270B4A274F +:10C07D000E4A27114A271420168D00A0EA20108D94 +:10C08D0000ACE5200A8D00A1C120048D00AFA38D69 +:10C09D0000A99B27C9873B00088D00BA948F7207B2 +:10C0AD0006462D8D00C97227054A270C2022A6337E +:10C0BD008D00C446A4BF2008A6338D00C446AA40F7 +:10C0CD008D00C98BA6338D00C9B8A6138D00C41879 +:10C0DD008D00A99B26068D00A85226BD725F064DC8 +:10C0ED00320008878D0086343B000A8D00A7A935E4 +:10C0FD000000013593000235AC00038D009C94AE19 +:10C10D0006DABF08A60C8D00A945443F00B7015FB4 +:10C11D00B60A975CB3002604A62B2002A6208D003C +:10C12D009B2DA10625DE8D009C8F32000AAC008769 +:10C13D003E72195005AA8072035203FBC752047256 +:10C14D00015203FBC652043D00272992BC0001B7E2 +:10C15D0004B6004AB70072035203FBB604C752047B +:10C16D0072015203FBC652045F5C72BB0002BF0238 +:10C17D003D0026D772185005878D0086D28D00C8D8 +:10C18D00FDA6068D00C446B708A6078D00C7FDA6FF +:10C19D00088D0086078D0087675FB60897BF02A6DA +:10C1AD00108D0086078D0085D48D00878EA6088D95 +:10C1BD0000C7FD8D0087748D0085D4AC0087393B99 +:10C1CD0000088D00BF448F72070646228D00C9AE50 +:10C1DD008D00C991A4FCB708AE064DF6A130270A13 +:10C1ED00A133260AB608AA03B7088D00C7828D00B1 +:10C1FD00A99B26068D00A85226C8725F064D3200F7 +:10C20D0008873B00088D00B9818F72070646218D86 +:10C21D0000C97227054A270C20168D00C99DA4F868 +:10C22D00AA0320088D00C99DA4F8AA058D00C70496 +:10C23D008D00A99B26068D00A85226C9725F064D5A +:10C24D00320008878D00BB1B8F72070646278D00B5 +:10C25D00C97227054A2710201C350A00008D00C819 +:10C26D00A6350A000020088D00C8A2350C0000A6D6 +:10C27D00148D00C4188D00A99B26068D00A852268A +:10C28D00C3725F064D873B00088D00BEDB8F7207C2 +:10C29D0006461D8D00C97227054A270A20128D00FA +:10C2AD00C997A4F720068D00C997AA088D00C73439 +:10C2BD008D00A99B26068D00A85226CD725F064DD6 +:10C2CD00320008873B00088D00BC278F7207064699 +:10C2DD001D8D00C97227054A270A20128D00C997A6 +:10C2ED00A4FB20068D00C997AA048D00C7348D00CC +:10C2FD00A99B26068D00A85226CD725F064D3200F1 +:10C30D0008873B000855525000088D00AEED8D009A +:10C31D00C7BC8D00C97F7218500C7218500D721960 +:10C32D00500A8D00C97F7218500A8D00C7BCB6081F +:10C33D00A50126048D00AF123200088735070004D1 +:10C34D00905F90978D00876A5F5C8D00C83AB60448 +:10C35D00938D008D059FA5012704A6312002A630DF +:10C36D0092BD0005B604B7004AB7043D0026D54F6F +:10C37D0092BD0001878D00AAAF8F72070646048D0E +:10C38D0000C9AEAE064DF6A1312706A13327082010 +:10C39D000A8D00BD9420048D00C1CC8D00A99B2673 +:10C3AD00068D00A85226CE725F064D877211525629 +:10C3BD00C6064E2708A6FFCB064EC7064EA606C4D8 +:10C3CD000646A1042608721506467210064672032B +:10C3DD00064608721306467214064680B701CE0053 +:10C3ED001272B00010A303F6250F5506480000B6D3 +:10C3FD0000A40134009011500F90CE0012935CCF29 +:10C40D0000128D00C883B60190F787A11824293535 +:10C41D00AE505335565053905F9097AE06508D00E9 +:10C42D008F2C9372BB0002BF02B60092BD00017249 +:10C43D00055054FB721750548772195005A47F7222 +:10C44D00035203FBC7520472015203FBC65204721E +:10C45D00035203FB725F520472015203FBC6520476 +:10C46D0072185005878D0086D28D00878E8D00C87D +:10C47D00FD450900A6068D00C539450A00A6078DA4 +:10C48D0000C539450B00A6088D00C539AC008739AC +:10C49D008D00876A2009720F5230FB8D00C8093F4D +:10C4AD00015FBF02BE06893B0005965C8D0096BB01 +:10C4BD008485270692BC000526DC87CE061672B051 +:10C4CD000614A3000324048D00C7F090CE06149328 +:10C4DD005CCF061451024F02A41F025172A9061817 +:10C4ED0090F6878A84A4BF88868D0086C88D0086C5 +:10C4FD00CD721152B6A602CA0648C706488D00AEC7 +:10C50D00C38D0087088D0086FD80720D5230FB7241 +:10C51D0017523572155235721B5235721B50C3723C +:10C52D001650027217500372155003877219500579 +:10C53D00AA808D00C6D372035203FBB600C7520406 +:10C54D0072015203FBC6520472185005878D008686 +:10C55D00D28D0086D78D0087918D00AAA720048DDE +:10C56D0000C5D48D00C68926F6AC008735AE0642CF +:10C57D008D008CC1A60D8D00C7A0A6118D0086184B +:10C58D008D00C7A4A6058D00C7A0B60287721350F3 +:10C59D000F7217500A7215500F7217500F721650F6 +:10C5AD000F7215500F72145005877213500F7217BA +:10C5BD00500A7215500F7217500F7216500F7215D8 +:10C5CD00500F72145005875F97BF0E5FBF0CAE0002 +:10C5DD000ABF065FBF048D00847D8D0087818D00AD +:10C5ED00C609871C00038D00C84C8D00C9C792BDBC +:10C5FD0000014F92BD000D8D00C937878D008462FB +:10C60D00AEFFD0BF065F5ABF048D008462B6084CE2 +:10C61D00B708878D00BFA935000001358D000235A3 +:10C62D00EE00038D00C49DA626878D00C6C28D0029 +:10C63D00C9C78D00C8BE8D00C49D8D00C7248772EB +:10C64D001850C3A603CA5201C75201A644CA5200CC +:10C65D00C7520087721A50A3720B500B027580A639 +:10C66D003FC40646C70646803B0008B7088D00C983 +:10C67D00CC4508008D00C9A3320008875FB6089726 +:10C68D008D00877772BB0006BF0692BC0005875CE4 +:10C69D0072BB0002F692BD0009905FB6008D00C816 +:10C6AD00B587A6268D00C4468D00C8D9A6268D0057 +:10C6BD00C4464444875C8D00C84C8D00C83692BD7D +:10C6CD000001AE00028772035203FBC752047201D0 +:10C6DD005203FBC652048735000001359700023521 +:10C6ED00D40003AC00BFA935000001358C00023524 +:10C6FD00720003AC00BFA98D00C98BA6318D00C996 +:10C70D0027A612AC00C4188D0087538D00C3498D28 +:10C71D00008753AC00BFA9350000013597000235E5 +:10C72D009C0003AC00C49D8D00C98BA6268D00C94D +:10C73D0027A60EAC00C41835000001359700023550 +:10C74D00B60003AC00C49D8D00C9858D00A0AE4E12 +:10C75D00A40F3F02B703878D00B8F3AE04143F0159 +:10C76D00BF02AC00BCA28D00C30F4F8D00C675A6D5 +:10C77D0080AC00C675450800A60C8D00C927A60B18 +:10C78D00AC00C4181C000372BB0000F692BD00097A +:10C79D00905F878D008607AE06428D0085EFAC0059 +:10C7AD008CD972115286A6FBC40648C70648808DE7 +:10C7BD0000C97F7219500C7219500D873500000198 +:10C7CD00359300023537000387350000013597009A +:10C7DD000235C4000387350000013597000235C2CC +:10C7ED00000387A601C40648A801449011500F8785 +:10C7FD008D00C4465F97BF025FBF0087C752315F90 +:10C80D005C72BB0006BF068772BB0002BF0292BC02 +:10C81D00000187AE06428D0085D7AC008CD98D0006 +:10C82D00C539AE0650AC008F2C8D00875372BB00FE +:10C83D0002BF02878D00C446B7008D00C7C987901F +:10C84D00BF0072BB0000F65F5C878990AE00118D52 +:10C85D000092B88587BF0292BC00018D00C98B87FD +:10C86D008D00876A72BB0006BF0687CE001272B0BC +:10C87D000010A30066875102A403015172A9001490 +:10C88D008735FF0000A622AC00C53935FF0000A694 +:10C89D0022AC00C539350C0000A637AC00C539A651 +:10C8AD00F7C40647C706478790975101A40F025159 +:10C8BD00874F92BD0001AC0087538D00BFA98D003D +:10C8CD00C6F4878D00C4468D00C8D9878D00C71466 +:10C8DD008D00C6E4878D00BFA9B608A501878D0020 +:10C8ED00C49D8D00C99187350100008D00C9A387B6 +:10C8FD008D00C8F48D00C8F487720F5230FBC752FB +:10C90D003187C60647B700A502878D00C947C70600 +:10C91D004D874EA40F3F00B701878D00C5394508DF +:10C92D0000878D00A344AC00BFA98D008753AC00D8 +:10C93D00C49D3F00A60DAC00C53972170646C6064C +:10C94D004F87CA0647C7064787CE0649A300028709 +:10C95D008D00C446B700878D00C815B700878D00C0 +:10C96D00BFA9B608878D00C917A03087A612AC00E5 +:10C97D00C446A601AC00AF1B5C8D00C85787B70835 +:10C98D0045080087A60CAC00C446A626AC00C446DC +:10C99D00A631AC00C446A601AC00C5398D00C985D1 +:10C9AD00878D00C917878D00A0AE878D00C9278799 +:10C9BD008D00C857878D00C7C9878D00C836878DF4 +:06C9CD0000C8F48775802C +:040000050000910066 +:00000001FF diff --git a/bridge.c b/bridge.c new file mode 100644 index 0000000..0edf156 --- /dev/null +++ b/bridge.c @@ -0,0 +1,115 @@ +#include "config.h" +#include "bridge.h" +#include "spi.h" +#include "rfm98w.h" +#include "uart.h" +#include "timer.h" + +#define IRQ_CHECK() ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & \ + (RF98_RX_DONE | RF98_VALID_HEADER) ) + +#define MODEMSTAT_CHECK() ( spi_readreg(RF98_REG_18_MODEM_STAT) & \ + RF98_MODEM_STATUS_SIGNAL_DETECTED ) + +static void slice_and_transmit(void); + +void bridge() +{ + uint8_t len; + + tim1_start(); + tim2_start(); + CBUF_Init(urxcbuf); + uart_stop(); + uart_init(cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK); + RTS_SET_LOW; + + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); // clear interrupts + spi_writereg(RF98_REG_0F_FIFO_RX_BASE_ADDR, RF_FIFO_RXBASE); + + /* Set header implicit flag */ + if ( spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & + RF98_IMPLICIT_HEADER_MODE_ON ) + { + flag.header_implicit = true; + /* max PayloadLength for receiving */ + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF); + } else + { + flag.header_implicit = false; + } + + rf_setopmode(RF98_MODE_RXCONTINUOUS); + while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) ); + RX_ON; + + do + { + /* process incoming Radio data */ + if ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_RX_DONE ) + { + tim1_restart(); + len = rf_recv(); + for (uint8_t i = 0; i < len; i++) + { + uart_putchar(rf_rxbuf[i]); + } + } + + /* process incoming UART data */ + while ( CBUF_Len(utmpbuf) && !flag.uart_rx_complete ) + { + cbuf_push(cbuf_pop2()); + } + + if ( flag.uart_rx_complete ) + { + tim1_restart(); + if ( !IRQ_CHECK() && !MODEMSTAT_CHECK() ) // transmit if not receiving + { + slice_and_transmit(); + } + } + adaptive_sleep(); + } while (flag.tnc_mode == MODE_BRIDGE); +} + +static void slice_and_transmit() +{ + uint16_t len; + uint8_t payload_sz = RF96_MTU; + + len = CBUF_Len(urxcbuf); + if ( flag.header_implicit ) + payload_sz--; + if ( len ) + { + while ( len > payload_sz ) + { + len -= payload_sz; + rf_send(rf_txbuf, assemble(payload_sz, 0)); + while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block + } + + if ( len > 0 ) + { + rf_send(rf_txbuf, assemble(len, 0)); + while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block + } + + /* Return to RX */ + RX_ON; + //rf_setopmode(RF98_MODE_STDBY); /* <-- after TX already in STDBY */ + spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */ + /* set maximum PayloadLength for receiving in implicit mode */ + if ( flag.header_implicit ) + { + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF); + } + rf_setopmode(RF98_MODE_RXCONTINUOUS); + while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) ); + } + + flag.uart_rx_complete = false; +} diff --git a/bridge.h b/bridge.h new file mode 100644 index 0000000..d959c61 --- /dev/null +++ b/bridge.h @@ -0,0 +1,7 @@ +// bridge.h +#ifndef BRIDGE_H +#define BRIDGE_H + +void bridge(void); + +#endif \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..0aad262 --- /dev/null +++ b/config.h @@ -0,0 +1,187 @@ +// config.h +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include +#include "CBUF.h" +#include "util.h" + +#define HIGH 1 +#define LOW 0 + + +#define F_MASTER 16 /* MCU clock */ +#define RF_XTAL 32000000.0 /* SX1278 crystal frq */ +#define RF_FIFO_TXBASE 0x00 +#define RF_FIFO_RXBASE 0x00 +#define RF96_MTU 255 /* LoRa MTU */ +#define RFBUF_SZ RF96_MTU + 1 /* radio buf size */ +#define urxcbuf_SIZE 1024 +#define utmpbuf_SIZE 32 +#define TTBL_SZ ( utmpbuf_SIZE / 10 ) /* tmp TX buffer low watermark*/ +#define TTBH_SZ ( utmpbuf_SIZE - 10 ) /* tmp TX buffer high watermark*/ +#define TBL_SZ ( urxcbuf_SIZE / 10 ) /* TX buffer low watermark*/ +#define TBH_SZ ( urxcbuf_SIZE - 10 ) /* TX buffer high watermark*/ +#define CFG_SZ 24 +#define MODE_SETUP 0 /* TNC SETUP MODE */ +#define MODE_BRIDGE 1 /* TNC TRANSPARENT MODE */ +#define MODE_KISS 2 /* TNC KISS MODE */ +#define P_PARAMETER cfg[CFG_PPR_IDX] +#define TXDELAY cfg[CFG_TXD_IDX] /* in 10 ms units */ +#define SLOTTIME cfg[CFG_SLT_IDX] /* in 10 ms units */ + +#define LNA_CTL PB_ODR_ODR2 +#define BIAS_CTL PC_ODR_ODR3 +#define NRST_CTL PC_ODR_ODR4 +#define M0_SW PC_IDR_IDR5 +#define M1_SW PC_IDR_IDR6 +#define AUX PD_ODR_ODR0 +#define PA_CTL PD_ODR_ODR1 +#define TR_SW10 PD_ODR_ODR2 /* TX/RX switch, STM8 pin 10 */ +#define TR_SW11 PD_ODR_ODR3 /* TX/RX switch, STM8 pin 11 */ +#define TCXO_CTL PD_ODR_ODR4 + +#define PA_ENABLED HIGH +#define PA_DISABLED LOW +#define LNA_ENABLED HIGH +#define LNA_DISABLED LOW +#define TCXO_ENABLED HIGH +#define TCXO_DISABLED LOW +#define BIAS_ON HIGH +#define BIAS_OFF LOW + +#define CFG_TNC_IDX 0 /* TNC parameters: mode, serial speed */ +#define CFG_RND_IDX 1 /* salt for random generator */ +#define CFG_TXD_IDX 2 /* TXDelay CSMA parameter */ +#define CFG_PPR_IDX 3 /* P-persistence CSMA parameter */ +#define CFG_SLT_IDX 4 /* SlotTime CSMA parameter */ +#define CFG_FRU_IDX 5 /* freq. MSByte */ +#define CFG_FRM_IDX 6 /* freq. mid byte */ +#define CFG_FRL_IDX 7 /* freq. LSbyte */ +#define CFG_PAC_IDX 8 /* PA config */ +#define CFG_PAR_IDX 9 /* PA ramp */ +#define CFG_OCP_IDX 10 /* overcurrent protection */ +#define CFG_LNA_IDX 11 /* LNA cfg */ +#define CFG_MC1_IDX 12 /* RegModemConfig1 */ +#define CFG_MC2_IDX 13 /* RegModemConfig2 */ +#define CFG_MC3_IDX 14 /* RegModemConfig3 */ +#define CFG_PRU_IDX 15 /* preamble length MSByte */ +#define CFG_PRL_IDX 16 /* -//- LSByte */ +#define CFG_PPM_IDX 17 /* PPM correction */ +#define CFG_DEO_IDX 18 /* DetectOptimize */ +#define CFG_IIQ_IDX 19 /* InvertIq */ +#define CFG_DTT_IDX 20 /* DetectionThreshold */ +#define CFG_SCW_IDX 21 /* SyncWord */ +#define CFG_R36_IDX 22 /* Register 0x36 (See SX127x Errata note, section 2.1.) */ +#define CFG_R3A_IDX 23 /* Register 0x3A (See SX127x Errata note, section 2.1.) */ + +#define CFG_TNC_SSP_MASK 0x07 +#define CFG_TNC_MOD_MASK 0x18 + +//-------------- Macros ----------------- +#define BIT(n) ( 1<<(n) ) +#define BIT_SET(y, mask) ( y |= (mask) ) +#define BIT_CLELAR(y, mask) ( y &= ~(mask) ) +#define BIT_FLIP(y, mask) ( y ^= (mask) ) +//! Create a bitmask of length +#define BIT_MASK(len) ( BIT(len)-1 ) +//! Create a bitfield mask of length starting at bit +#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) +//! Prepare a bitmask for insertion or combining +#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) +//! Extract a bitfield of length starting at bit from y +#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) +//! Insert a new bitfield value x into y +#define BF_SET(y, x, start, len) \ + ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) +//! Massage x for use in bitfield +#define BFN_PREP(x, name) ( ((x)<>name##_SHIFT ) +//! Set bitfield name from y to x: y.name= x. +#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) ) + +#define __wait_for_interrupt() asm("wfi") +#define __disable_interrupt() asm("sim") +#define __enable_interrupt() asm("rim") +#define __halt() asm("halt") +#define __reset() asm("dc8 $75") /* Initiate MCU RESET by illegal opcode */ +#define HIBYTE(x) ((char*)(&(x)))[0] +#define LOBYTE(x) ((char*)(&(x)))[1] + +#define TX_OFF \ + PA_CTL = PA_DISABLED; \ + BIAS_CTL = BIAS_OFF; \ + TR_SW10 = LOW; \ + TR_SW11 = LOW +#define TX_ON \ + RX_OFF; \ + TR_SW10 = HIGH; \ + TR_SW11 = LOW; \ + PA_CTL = PA_ENABLED; \ + BIAS_CTL = BIAS_ON +#define RX_OFF \ + LNA_CTL = LNA_DISABLED; \ + TR_SW10 = LOW; \ + TR_SW11 = LOW +#define RX_ON \ + TX_OFF; \ + TR_SW11 = HIGH; \ + TR_SW10 = LOW; \ + LNA_CTL = LNA_ENABLED + +#define RTS_SET_HIGH \ + AUX = flag.rts_inverted ? HIGH : LOW; +#define RTS_SET_LOW \ + AUX = flag.rts_inverted ? LOW : HIGH; + +extern uint32_t seed; +extern struct flag_t flag; +extern volatile struct urxcbuf_t urxcbuf; +extern volatile struct utmpbuf_t utmpbuf; +extern char rf_rxbuf[RFBUF_SZ]; +extern char rf_txbuf[RFBUF_SZ]; +extern uint8_t *cfg; + +struct flag_t { + unsigned char uart_rx_complete : 1; /* UART IDLE char received */ + unsigned char uart_timer_restarted : 1; /* serial symbol timeout */ + unsigned char uart_timer_pending : 1; /* serial symbol timeout */ + unsigned char uart_rxne : 1; /* UART RX buf not empty */ + unsigned char txdelay_engaged : 1; + unsigned char slottime_engaged : 1; + unsigned char tnc_mode : 2; + unsigned char kiss_sof : 1; /* Start of Frame */ + unsigned char kiss_payload : 1; + unsigned char kiss_escaped : 1; + unsigned char kiss_frame_ready : 1; + unsigned char kiss_frame_first : 1; + unsigned char kiss_start_sent : 1; + unsigned char header_implicit : 1; + unsigned char rf_multiframe : 1; + unsigned char rts_inverted : 1; /* RTS output inverted */ + unsigned char tfc_timeout : 1; /* traffic timeout */ + unsigned char t1s_reset : 1; /* 1s timer reset */ +}; + +/* UART RX circular buffer */ +struct urxcbuf_t { + uint16_t m_getIdx; + uint16_t m_putIdx; + uint8_t m_entry[ urxcbuf_SIZE ]; +}; + +/* tmp UART RX circular buffer */ +struct utmpbuf_t { + uint16_t m_getIdx; + uint16_t m_putIdx; + uint8_t m_entry[ utmpbuf_SIZE ]; +}; + +void reset_radio(void); +void eeprom_populate(void); +void eeprom_write_byte(uint8_t, uint8_t); + +#endif diff --git a/intr.c b/intr.c new file mode 100644 index 0000000..e75ac72 --- /dev/null +++ b/intr.c @@ -0,0 +1,78 @@ +#include "config.h" +#include "uart.h" +#include "timer.h" +#include "kiss.h" + +#pragma vector=USART_R_RXNE_vector +__interrupt void USART_R_RXNE_handler(void) { + if (USART1_SR & MASK_USART1_SR_RXNE) { + flag.uart_rxne = true; + uart_dr = USART1_DR; + /* Hardware flow control */ + if ( CBUF_Len(utmpbuf) >= TTBH_SZ ) + { + RTS_SET_HIGH; + } + CBUF_Push(utmpbuf, uart_dr); + } + else { + USART1_DR; // implicit read + } + flag.uart_timer_restarted = true; +} + +#pragma vector=EXTI5_vector // Pin 5 interrupts, see datasheet +__interrupt void EXTI5_handler(void) +{ + /* M0 Switch handler */ + EXTI_SR1_P5F = 1; // Clear the interrupt flag + if ( M0_SW == HIGH ) + __reset(); + else + flag.tnc_mode = MODE_SETUP; +} + +#pragma vector=EXTI6_vector // Pin 6 interrupts, see datasheet +__interrupt void EXTI6_handler(void) +{ + /* M1 Switch handler */ + __reset(); +} + +#pragma vector=TIM1_OVR_UIF_vector +__interrupt void TIM1_OVR_UIF_handler(void) +{ + TIM1_SR1_UIF = 0; //clear interrupt flag + flag.tfc_timeout = true; + tim1_stop(); +} + +#pragma vector=TIM2_OVR_UIF_vector +__interrupt void TIM2_OVR_UIF_handler(void) +{ + TIM2_SR1_UIF = 0; //clear interrupt flag + + if (tim2_cnt) + { + tim2_cnt--; + } + + if ( flag.uart_timer_pending && !flag.uart_timer_restarted ) + { + flag.uart_timer_pending = false; + flag.uart_rx_complete = true; + } + + if ( flag.uart_timer_restarted ) + { + flag.uart_timer_restarted = false; + flag.uart_timer_pending = true; + } +} + +#pragma vector=TIM3_OVR_UIF_vector +__interrupt void TIM3_OVR_UIF_handler(void) +{ + TIM3_SR1_UIF = 0; //clear interrupt flag + flag.t1s_reset = false; +} diff --git a/intr.h b/intr.h new file mode 100644 index 0000000..eb4b944 --- /dev/null +++ b/intr.h @@ -0,0 +1,5 @@ +// intr.h +#ifndef INTR_H +#define INTR_H + +#endif diff --git a/kiss.c b/kiss.c new file mode 100644 index 0000000..0e09766 --- /dev/null +++ b/kiss.c @@ -0,0 +1,335 @@ +#include "config.h" +#include "kiss.h" +#include "spi.h" +#include "rfm98w.h" +#include "uart.h" +#include "timer.h" + +// Radio frame first byte: +// 2 - first multiframe +// 1 - intermediate multiframe +// 0 - last multiframe +// other value - single frame + + +#define IRQ_CHECK() ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & \ + (RF98_RX_DONE | RF98_VALID_HEADER) ) + +#define MODEMSTAT_CHECK() ( spi_readreg(RF98_REG_18_MODEM_STAT) & \ + RF98_MODEM_STATUS_SIGNAL_DETECTED ) + +static uint16_t pkt_len; // length of packet to transmit via RF +static uint16_t tmp_len; + +static void kiss_in(uint8_t); +static void kiss_out(uint8_t); +static void kiss_process_cmd(void); +static void csma_transmit(void); +static void cmd_set_txdelay(void); +static void cmd_set_p(void); +static void cmd_set_slottime(void); + +void kiss() +{ + flag.txdelay_engaged = false; + flag.slottime_engaged = false; + flag.kiss_start_sent = false; + + tim1_start(); + tim2_start(); + CBUF_Init(urxcbuf); + uart_stop(); + uart_init(cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK); + RTS_SET_LOW; + + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); // clear interrupts + spi_writereg(RF98_REG_0F_FIFO_RX_BASE_ADDR, RF_FIFO_RXBASE); + + /* Set header implicit flag */ + if ( spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & + RF98_IMPLICIT_HEADER_MODE_ON ) + { + flag.header_implicit = true; + /* max PayloadLength for receiving */ + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF); + } else + { + flag.header_implicit = false; + } + + rf_setopmode(RF98_MODE_RXCONTINUOUS); + while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) ); + RX_ON; + + do + { + /* process incoming radio data */ + if ( spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_RX_DONE ) + { + kiss_out(rf_recv()); + } + + /* process incoming UART data */ + while ( CBUF_Len(utmpbuf) && !flag.kiss_frame_ready ) + { + kiss_in(cbuf_pop2()); + } + + if ( flag.kiss_frame_ready ) + { + tim1_restart(); + kiss_process_cmd(); + } + adaptive_sleep(); + } while (flag.tnc_mode == MODE_KISS); +} + +static void kiss_in(uint8_t byte) +{ + switch(byte) + { + case FEND: + if ( flag.kiss_payload ) + { + /* EOF received */ + flag.kiss_sof = false; + flag.kiss_payload = false; + flag.kiss_escaped = false; + flag.kiss_frame_ready = true; + pkt_len = tmp_len; + } + else { + /* SOF received */ + tmp_len = 0; + flag.kiss_sof = true; + } + break; + case FESC: + if ( flag.kiss_payload ) + { + flag.kiss_escaped = true; + } + break; + default: + if ( flag.kiss_sof ) + { + flag.kiss_sof = false; + flag.kiss_payload = true; + } + + if ( flag.kiss_payload ) + { + if ( !flag.kiss_escaped ) + { + cbuf_push(byte); + tmp_len++; + } + else { + flag.kiss_escaped = false; + + switch ( byte ) + { + case TFESC: + cbuf_push(FESC); + tmp_len++; + break; + case TFEND: + cbuf_push(FEND); + tmp_len++; + break; + default: + // framing error, drop the frame + flag.kiss_sof = false; + flag.kiss_payload = false; + flag.kiss_escaped = false; + flag.kiss_frame_ready = false; + } // switch + + } // else kiss_escaped + } // if kiss_payload + + } // switch +} + +static void kiss_out(const uint8_t len) +{ + uint8_t idx = 0; + + if ( rf_rxbuf[0] >= FRAME_FIRST ) + { + if ( flag.kiss_start_sent ) // error in framing, start over + uart_putchar(FEND); + + uart_putchar(FEND); + uart_putchar(CMD_DATA_FRAME); + flag.kiss_start_sent = true; + } + + /* check for multiframe */ + if ( rf_rxbuf[0] <= FRAME_FIRST ) + idx++; + + for (uint8_t i = idx; i < len; i++) + { + switch ( rf_rxbuf[i] ) + { + case FESC: + uart_putchar(FESC); + uart_putchar(TFESC); + break; + case FEND: + uart_putchar(FESC); + uart_putchar(TFEND); + break; + default: + uart_putchar(rf_rxbuf[i]); + } + } + + /* last or single frame end */ + if ( rf_rxbuf[0] == FRAME_LAST || rf_rxbuf[0] > FRAME_FIRST ) + { + uart_putchar(FEND); + flag.kiss_start_sent = false; + } +} + +static void csma_transmit() +{ + uint16_t len; + uint8_t payload_sz = RF96_MTU; + + if ( (flag.txdelay_engaged || flag.slottime_engaged) && tim2_cnt ) + { + /* TxDelay or SlotTime is not expired */ + return; + } + + /* Delay is expired */ + if ( flag.txdelay_engaged || flag.slottime_engaged ) + { + flag.txdelay_engaged = false; + + if ( flag.slottime_engaged ) + { + flag.slottime_engaged = false; + + if ( IRQ_CHECK() || MODEMSTAT_CHECK() ) + { + /* Oops, are we still receiving? */ + return; + } + } + + /* first byte is KISS CMD */ + if ( pkt_len > 1 ) /* frame size >0 */ + { + /* get rid of KISS CMD byte */ + CBUF_AdvancePopIdx( urxcbuf ); + len = pkt_len-1; + if ( flag.header_implicit ) + payload_sz--; + + if ( pkt_len > payload_sz ) + { + flag.rf_multiframe = true; + payload_sz--; + } + else + flag.rf_multiframe = false; + + flag.kiss_frame_first = true; + while ( len > payload_sz ) + { + len -= payload_sz; + if ( flag.kiss_frame_first ) + { + flag.kiss_frame_first = false; + rf_send(rf_txbuf, assemble(payload_sz, FRAME_FIRST)); // blocking op + } + else + rf_send(rf_txbuf, assemble(payload_sz, FRAME_INTER)); // blocking op + } + rf_send(rf_txbuf, assemble(len, FRAME_LAST)); // blocking op + /* Return to RX */ + flag.kiss_frame_ready = false; + RX_ON; + //rf_setopmode(RF98_MODE_STDBY); /* <-- after TX already in STDBY */ + spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */ + /* set maximum PayloadLength for receiving in implicit mode */ + if ( flag.header_implicit ) + { + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF); + } + rf_setopmode(RF98_MODE_RXCONTINUOUS); + while ( spi_readreg(RF98_REG_01_OP_MODE) != (RF98_LONG_RANGE_MODE | RF98_MODE_RXCONTINUOUS) ); + } + else { + flag.kiss_frame_ready = false; + } + } + else { + // p-persistence CSMA + if ( random() <= P_PARAMETER ) + { + flag.txdelay_engaged = true; + tim2_restart(TXDELAY); + } else + { + flag.slottime_engaged = true; + tim2_restart(SLOTTIME); + } + } +} + +static void kiss_process_cmd() +{ + // first byte is KISS cmd + switch ( CBUF_Get( urxcbuf, 0 ) ) + { + case CMD_DATA_FRAME: + csma_transmit(); + break; + case CMD_TX_DELAY: + cmd_set_txdelay(); + flag.kiss_frame_ready = false; + break; + case CMD_P: + cmd_set_p(); + flag.kiss_frame_ready = false; + break; + case CMD_SLOT_TIME: + cmd_set_slottime(); + flag.kiss_frame_ready = false; + break; + default: + flag.kiss_frame_ready = false; + /* Pop out unprocessed packet data */ + urxcbuf.m_getIdx += pkt_len; + } +} + +static void cmd_set_txdelay() +{ + if ( pkt_len == 2 ) + { + eeprom_write_byte(CFG_TXD_IDX, rf_txbuf[1]); + } +} + +static void cmd_set_p() +{ + if ( pkt_len == 2 ) + { + eeprom_write_byte(CFG_PPR_IDX, rf_txbuf[1]); + } +} + +static void cmd_set_slottime() +{ + if ( pkt_len == 2 ) + { + eeprom_write_byte(CFG_SLT_IDX, rf_txbuf[1]); + } +} diff --git a/kiss.h b/kiss.h new file mode 100644 index 0000000..59f4919 --- /dev/null +++ b/kiss.h @@ -0,0 +1,27 @@ +// kiss.h +#ifndef KISS_H +#define KISS_H + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define CMD_DATA_FRAME 0x00 +#define CMD_TX_DELAY 0x01 +#define CMD_P 0x02 +#define CMD_SLOT_TIME 0x03 +#define CMD_SET_HARDWARE 0x06 +#define CMD_SET_FEC 0x08 + +#define CMD_SF_MASK 0xA0 +#define CMD_BW_MASK 0xB0 +#define CMD_PREAMBLE_MASK 0xC0 + +#define FRAME_FIRST 2 /* First frame */ +#define FRAME_INTER 1 /* Intermediate frame */ +#define FRAME_LAST 0 /* last frame */ + +void kiss(void); + +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..08fb27e --- /dev/null +++ b/main.c @@ -0,0 +1,268 @@ +// STM8L 433T30D KISS TNC +// Version 1.0: June-July 2018 (c) UR5VIB +// * initial release for RFM98 +// Version 1.1 September 2018 +// * added UART hardware flow control +// * improved performance +// +// E32-TTL-1W model 433T30D +// Due to poor system design it requires UART hardware flow ctl. +// DIOx are unpopulated on the PCB which impacts performance. +// +// STM8L pinout: +// 1 - NRST 8 - AUX 15 - 22 - +// 2 - TX 9 - PA_ON 16 - NSS 23 - +// 3 - RX 10 - RX_ON 17 - SCK 24 - BIAS +// 4 - 11 - TX_ON 18 - MOSI 25 - RST +// 5 - 12 - 19 - MISO 26 - M0 +// 6 - 13 - 20 - TCX_ON 27 - M1 +// 7 - 14 - LNA_ON 21 - 28 - SWIM + +#include "config.h" +#include "timer.h" +#include "uart.h" +#include "spi.h" +#include "rfm98w.h" +#include "menue.h" +#include "bridge.h" +#include "kiss.h" + +struct flag_t flag; +volatile struct urxcbuf_t urxcbuf; +volatile struct utmpbuf_t utmpbuf; +char rf_rxbuf[RFBUF_SZ]; +char rf_txbuf[RFBUF_SZ]; + +uint8_t *cfg = (uint8_t *) 0x1000; // EEPROM base address. + +static void mcu_init(void); +static void modemcfg_populate(void); +void reset_radio(void); + +uint32_t seed; + +void main( void ) +{ + mcu_init(); + + // TTL or RS-232 flow ctl + if ( M1_SW == HIGH ) + flag.rts_inverted = true; + else + flag.rts_inverted = false; + + TCXO_CTL = TCXO_ENABLED; + spi_init(); + tim2_start(); + + // Reset SX1278 + reset_radio(); + // Set LoRa mode + rf_setopmode(RF98_MODE_SLEEP); + rf_setopmode(RF98_LONG_RANGE_MODE); + + // Load config into the radio + modemcfg_populate(); + + /* Update random() salt */ + rf_setopmode(RF98_MODE_RXCONTINUOUS); + delay_10ms(1); + seed = ((uint32_t)cfg[CFG_RND_IDX] << 16); + seed |= (spi_readreg(RF98_REG_1B_RSSI_VALUE) << 8); + seed |= spi_readreg(RF98_REG_2C_RSSI_WIDEBAND); + rf_setopmode(RF98_MODE_SLEEP); + if ( seed == 0 ) + seed = 0x07; /* kludge */ + /* save it for next use */ + if ( (uint8_t)seed ) + { + eeprom_write_byte(CFG_RND_IDX, (uint8_t)seed); + } + + /* Set initial TNC mode according to switch positon */ + if ( M0_SW == HIGH ) + { + flag.tnc_mode = ((cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3); + } else + { + flag.tnc_mode = MODE_SETUP; + } + + /* Main programme loop */ + while(1) + { + switch ( flag.tnc_mode ) + { + case MODE_SETUP: + menue(); + break; + case MODE_BRIDGE: + bridge(); + break; + case MODE_KISS: + kiss(); + break; + default: + __wait_for_interrupt(); + } + } +} + +void mcu_init() +{ + CLK_CKDIVR = 0; // 16 MHz + + /* Configure unused pins as Out Push-Pull, Low. See RefMan. sect. 10.5. */ + PA_DDR = 0xFF; // OUT + PA_CR1 = 0xFF; // P-P + PA_ODR = 0x00; // LOW + PB_DDR = 0xFF; // OUT + PB_CR1 = 0xFF; // P-P + PB_ODR = 0x00; // LOW + PC_DDR = 0xFF; // OUT + PC_CR1 = 0xFF; // P-P + PC_ODR = 0x00; // LOW + PD_DDR = 0xFF; // OUT + PD_CR1 = 0xFF; // P-P + PD_ODR = 0x00; // LOW + + /* PA1 = RESET */ + PA_DDR_DDR1 = 0; // IN + PA_CR1_C11 = 1; // P-U + + /* PB4 = CS */ + PB_DDR_DDR4 = 1; // OUT + PB_CR1_C14 = 1; // PUSH-PULL + PB_ODR_ODR4 = HIGH; // HIGH + /* PB5 = SCK */ + PB_DDR_DDR5 = 1; // OUT + PB_CR1_C15 = 1; // PUSH-PULL + PB_CR2_C25 = 1; // 10 MHz + PB_ODR_ODR5 = LOW; // LOW + /* PB6 = MOSI */ + PB_DDR_DDR6 = 1; // OUT + PB_CR1_C16 = 1; // PUSH-PULL + PB_CR2_C26 = 1; // 10 MHz + PB_ODR_ODR6 = LOW; // LOW + /* PB7 = MISO */ + PB_DDR_DDR7 = 0; // IN + PB_CR1_C17 = 1; // PULL-UP + + /* PB2 = LNA_CTL */ + PB_DDR_DDR2 = 1; // OUT + PB_CR1_C12 = 1; // PUSH-PULL + /* PC3 = PA BIAS_CTL */ + PC_DDR_DDR3 = 1; // OUT + PC_CR1_C13 = 1; // PUSH-PULL + /* PC4 = NRST_CTL */ + PC_DDR_DDR4 = 0; // IN + PC_CR1_C14 = 0; // FLOAT + /* PC5 = M0_SW */ + PC_DDR_DDR5 = 0; // IN + PC_CR1_C15 = 0; // FLOAT + PC_CR2_C25 = 1; // EXTI + EXTI_CR2_P5IS = 3; // int on falling & rising edge + /* PC6 = M1_SW */ + PC_DDR_DDR6 = 0; // IN + PC_CR1_C16 = 0; // FLOAT + PC_CR2_C26 = 1; // EXTI + EXTI_CR2_P6IS = 3; // int on falling & rising edge + /* PD0 = AUX */ + PD_DDR_DDR0 = 1; // OUT + PD_CR1_C10 = 1; // PUSH-PULL + /* PD1 = PA_CTL */ + PD_DDR_DDR1 = 1; // OUT + PD_CR1_C11 = 1; // PUSH-PULL + /* PD2 = TR_SW10 TX/RX Switch */ + PD_DDR_DDR2 = 1; // OUT + PD_CR1_C12 = 1; // PUSH-PULL + /* PD3 = TR_SW11 TX/RX Switch */ + PD_DDR_DDR3 = 1; // OUT + PD_CR1_C13 = 1; // PUSH-PULL + /* PD4 = TCXO_CTL */ + PD_DDR_DDR4 = 1; // OUT + PD_CR1_C14 = 1; // PUSH-PULL + + __enable_interrupt(); +} + +void eeprom_populate() +{ + reset_radio(); + rf_setopmode(RF98_MODE_SLEEP); + rf_setopmode(RF98_LONG_RANGE_MODE); + + eeprom_write_byte(CFG_FRU_IDX, spi_readreg(RF98_REG_06_FRF_MSB)); + eeprom_write_byte(CFG_FRM_IDX, spi_readreg(RF98_REG_07_FRF_MID)); + eeprom_write_byte(CFG_FRL_IDX, spi_readreg(RF98_REG_08_FRF_LSB)); + eeprom_write_byte(CFG_PAC_IDX, spi_readreg(RF98_REG_09_PA_CONFIG)); + eeprom_write_byte(CFG_PAR_IDX, spi_readreg(RF98_REG_0A_PA_RAMP)); + eeprom_write_byte(CFG_OCP_IDX, spi_readreg(RF98_REG_0B_OCP)); + eeprom_write_byte(CFG_LNA_IDX, spi_readreg(RF98_REG_0C_LNA)); + eeprom_write_byte(CFG_MC1_IDX, spi_readreg(RF98_REG_1D_MODEM_CONFIG1)); + eeprom_write_byte(CFG_MC2_IDX, spi_readreg(RF98_REG_1E_MODEM_CONFIG2)); + eeprom_write_byte(CFG_MC3_IDX, spi_readreg(RF98_REG_26_MODEM_CONFIG3)); + eeprom_write_byte(CFG_PRU_IDX, spi_readreg(RF98_REG_20_PREAMBLE_MSB)); + eeprom_write_byte(CFG_PRL_IDX, spi_readreg(RF98_REG_21_PREAMBLE_LSB)); + eeprom_write_byte(CFG_PPM_IDX, spi_readreg(RF98_REG_27_PPM_CORRECTION)); + eeprom_write_byte(CFG_DEO_IDX, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ)); + eeprom_write_byte(CFG_IIQ_IDX, spi_readreg(RF98_REG_33_INVERT_IQ)); + eeprom_write_byte(CFG_DTT_IDX, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD)); + eeprom_write_byte(CFG_SCW_IDX, spi_readreg(RF98_REG_39_SYNC_WORD)); + eeprom_write_byte(CFG_R36_IDX, spi_readreg(RF98_REG_36)); + eeprom_write_byte(CFG_R3A_IDX, spi_readreg(RF98_REG_3A)); +} + +void modemcfg_populate() +{ + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_06_FRF_MSB, cfg[CFG_FRU_IDX]); + spi_writereg(RF98_REG_07_FRF_MID, cfg[CFG_FRM_IDX]); + spi_writereg(RF98_REG_08_FRF_LSB, cfg[CFG_FRL_IDX]); + spi_writereg(RF98_REG_09_PA_CONFIG, cfg[CFG_PAC_IDX]); + spi_writereg(RF98_REG_0A_PA_RAMP, cfg[CFG_PAR_IDX]); + spi_writereg(RF98_REG_0B_OCP, cfg[CFG_OCP_IDX]); + spi_writereg(RF98_REG_0C_LNA, cfg[CFG_LNA_IDX]); + spi_writereg(RF98_REG_1D_MODEM_CONFIG1, cfg[CFG_MC1_IDX]); + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, cfg[CFG_MC2_IDX]); + spi_writereg(RF98_REG_26_MODEM_CONFIG3, cfg[CFG_MC3_IDX]); + spi_writereg(RF98_REG_20_PREAMBLE_MSB, cfg[CFG_PRU_IDX]); + spi_writereg(RF98_REG_21_PREAMBLE_LSB, cfg[CFG_PRL_IDX]); + spi_writereg(RF98_REG_27_PPM_CORRECTION, cfg[CFG_PPM_IDX]); + spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, cfg[CFG_DEO_IDX]); + spi_writereg(RF98_REG_33_INVERT_IQ, cfg[CFG_IIQ_IDX]); + spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, cfg[CFG_DTT_IDX]); + spi_writereg(RF98_REG_36, cfg[CFG_R36_IDX]); + if ( spi_readreg(RF98_REG_36) == RF98_REG_36_500KHZ ) + { + spi_writereg(RF98_REG_3A, cfg[CFG_R3A_IDX]); + } +} + +void reset_radio() +{ + uint8_t tim2_state = TIM2_CR1_CEN; + + tim2_start(); + delay_10ms(1); + PC_DDR_DDR4 = 0; // IN + PC_CR1_C14 = 0; // FLOAT + + delay_10ms(1); + PC_DDR_DDR4 = 1; // OUT + PC_CR1_C14 = 1; // P-P + + NRST_CTL = LOW; // PULL LOW + delay_10ms(1); + NRST_CTL = HIGH; // PULL HIGH + delay_10ms(1); + + PC_DDR_DDR4 = 0; // IN + PC_CR1_C14 = 0; // FLOAT + + /* return TIM2 previous state */ + if ( tim2_state == 0 ) + { + tim2_stop(); + } +} diff --git a/menue.c b/menue.c new file mode 100644 index 0000000..676cd32 --- /dev/null +++ b/menue.c @@ -0,0 +1,1800 @@ +#include "config.h" +#include "menue.h" +#include "uart.h" +#include "rfm98w.h" +#include "spi.h" +#include "timer.h" + +#define SIO_SP_SZ 8 +#define DIG_SZ 10 +#define SF_SZ 7 +#define CR_SZ 4 +#define BW_SZ 10 +#define RAMP_SZ 16 +#define LNA_SZ 6 + +static char input; +static char dig[DIG_SZ]; + +char *sio_speeds[SIO_SP_SZ] = { + "9600", + "19200", + "38400", + "57600", + "115200", + "230400", + "460800", + "921600" }; + +static char *sf_levels[SF_SZ] = { + "6 (64 CPS)", + "7 (128 CPS)", + "8 (256 CPS)", + "9 (512 CPS)", + "10 (1024 CPS)", + "11 (2048 CPS)", + "12 (4096 CPS)" }; + +static char *cr_levels[CR_SZ] = { + "4/5 (x 1.25 overhead)", + "4/6 (x 1.5 overhead)", + "4/7 (x 1.75 overhead)", + "4/8 (x 2 overhead)" }; + +static char *bw_levels[BW_SZ] = { + "7.8 kHz", + "10.4 kHz", + "15.6 kHz", + "20.8 kHz", + "31.25 kHz", + "41.7 kHz", + "62.5 kHz", + "125 kHz", + "250 kHz", + "500 kHz" }; + +static char *ramp_levels[RAMP_SZ] = { + "0000 -> 3.4 ms", + "0001 -> 2 ms", + "0010 -> 1 ms", + "0011 -> 500 us", + "0100 -> 250 us", + "0101 -> 125 us", + "0110 -> 100 us", + "0111 -> 62 us", + "1000 -> 50 us", + "1001 -> 40 us", + "1010 -> 31 us", + "1011 -> 25 us", + "1100 -> 20 us", + "1101 -> 15 us", + "1110 -> 12 us", + "1111 -> 10 us" }; + +static char *lna_levels[LNA_SZ] = { + "001 -> G1 = maximum gain", + "010 -> G2", + "011 -> G3", + "100 -> G4", + "101 -> G5", + "110 -> G6 = minimum gain" }; + +uint32_t atoi_simple(char *str) +{ + uint32_t res = 0; + + for (uint8_t i = 0; str[i] != '\0'; ++i) + res = res*10 + str[i] - '0'; + return res; +} + +void itob_simple(char *dst, uint8_t x) +{ + uint8_t i=7; + do + { + *dst++ = (x >> i & 1) ? '1' : '0'; + } while(i--); + *dst = '\0'; +} +/* +static void itox_simple(char *s, uint8_t i) +{ + uint8_t n; + s += 4; + *s = '\0'; + for (n = 4; n != 0; --n) { + *--s = "0123456789ABCDEF"[i & 0x0F]; + i >>= 4; + } +} +*/ + +static void itox_simple(char *s, uint8_t i) +{ + char hex[] = "0123456789ABCDEF"; + + s[0] = hex[i >> 4]; + s[1] = hex[i & 0x0F]; + s[2] = 0; +} + +static char *itoa_simple_helper(char *dest, long int i) { + if (i <= -10) { + dest = itoa_simple_helper(dest, i/10); + } + *dest++ = '0' - i%10; + return dest; +} + +char *itoa_simple(char *dest, long int i) { + char *s = dest; + if (i < 0) { + *s++ = '-'; + } else { + i = -i; + } + *itoa_simple_helper(s, i) = '\0'; + return dest; +} + +static void clearScreen() +{ + uart_putchar(27); // ESC + uart_puts("[2J"); // clear screen +} + +static void goHome() +{ + uart_putchar(27); // ESC + uart_puts("[H"); // cursor to home +} + +static void menue_basic_serial_draw() +{ + uint8_t i; + + clearScreen(); + goHome(); + uart_println("TNC serial port speed:"); + uart_println(""); + for (i=0;i= '1') && (input < ('1' + SIO_SP_SZ)) ) + { + t = cfg[CFG_TNC_IDX] & ~(CFG_TNC_SSP_MASK); + t |= (uint8_t)(input - '1'); + eeprom_write_byte(CFG_TNC_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_basic_mode_draw() +{ + uint8_t mode; + + clearScreen(); + goHome(); + uart_println("TNC mode:"); + uart_println(""); + mode = (cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3; + ( mode == MODE_BRIDGE ) ? uart_putchar('+') : uart_putchar(' '); + uart_println("1 - Transparent RF UART bridge"); + ( mode == MODE_KISS ) ? uart_putchar('+') : uart_putchar(' '); + uart_println("2 - KISS TNC"); + uart_println(" [ENTER] - Return to the previous menue"); +} + +static void menue_basic_mode() +{ + uint8_t mode; + + do + { + menue_basic_mode_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) + { + case '1': + mode = cfg[CFG_TNC_IDX] & ~(CFG_TNC_MOD_MASK); + mode |= (MODE_BRIDGE << 3); + eeprom_write_byte(CFG_TNC_IDX, mode); + break; + case '2': + mode = cfg[CFG_TNC_IDX] & ~(CFG_TNC_MOD_MASK); + mode |= (MODE_KISS << 3); + eeprom_write_byte(CFG_TNC_IDX, mode); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_basic_freq_draw() +{ + uint32_t frq_i; + + clearScreen(); + goHome(); + uart_println("Modem frequency:"); + uart_println(""); + uart_puts("Current frequency: "); + frq_i = (uint32_t)(rf_getfreq() * RF_STEP / 1000.0 ); + itoa_simple(dig, frq_i); + uart_puts(dig); + uart_println(" kHz"); + uart_puts("Enter new frequency in kHz or hit [ENTER] to return: "); +} + +static void menue_basic_freq() +{ + uint32_t frq_i, frq_s; + + do + { + menue_basic_freq_draw(); + uart_readln(dig, DIG_SZ); + frq_i = atoi_simple(dig); + if ( frq_i != 0 ) + { + frq_s = (uint32_t)(frq_i * 1000.0 / RF_STEP); + rf_setfreq(frq_s); + eeprom_write_byte(CFG_FRU_IDX, (uint8_t)(frq_s >> 16)); + eeprom_write_byte(CFG_FRM_IDX, (uint8_t)(frq_s >> 8)); + eeprom_write_byte(CFG_FRL_IDX, (uint8_t)frq_s); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_basic_sf_draw() +{ + uint8_t i; + + clearScreen(); + goHome(); + uart_println("Modem spreading factor in chips per symbol:"); + uart_println(""); + for (i=0;i> 4) & 0x0F) == (i+6) ) + uart_putchar('+'); + else + uart_putchar(' '); + uart_putchar(i+'1'); + uart_puts(" - "); + uart_println(sf_levels[i]); + } + uart_println(" [ENTER] - Return to the previous menue"); +} + +static void menue_basic_sf() +{ + uint8_t t; + + do + { + menue_basic_sf_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + + t = spi_readreg(RF98_REG_1E_MODEM_CONFIG2) & + ~(RF98_SPREADING_FACTOR_MASK); + if ( input == '1' ) + { + /* SF6 is a special case, see datasheet section 4.1.1.2. */ + /* set SF */ + t |= RF98_SPREADING_FACTOR_64CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + /* set implicit header */ + t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & + ~(RF98_IMPLICIT_HEADER_MODE_ON_MASK); + t |= RF98_IMPLICIT_HEADER_MODE_ON; + spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t); + eeprom_write_byte(CFG_MC1_IDX, t); + /* set DetectOptimize */ + t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ); + t &= ~(RF98_DETOPTIMIZE_MASK); + t |= RF98_DETOPTIMIZE_05; + spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t); + eeprom_write_byte(CFG_DEO_IDX, t); + /* set DetectionThreshold */ + spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, + RF98_DETTH_0C); + eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0C); + } else if ( (input >= '2') && (input <= '7') ) + { + /* set SF */ + switch ( input ) + { + case '2': + t |= RF98_SPREADING_FACTOR_128CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + case '3': + t |= RF98_SPREADING_FACTOR_256CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + case '4': + t |= RF98_SPREADING_FACTOR_512CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + case '5': + t |= RF98_SPREADING_FACTOR_1024CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + case '6': + t |= RF98_SPREADING_FACTOR_2048CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + case '7': + t |= RF98_SPREADING_FACTOR_4096CPS; + t |= RF98_PAYLOAD_CRC_ON; // we need CRC + spi_writereg(RF98_REG_1E_MODEM_CONFIG2, t); + eeprom_write_byte(CFG_MC2_IDX, t); + break; + } + /* set explicit header */ + t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & + ~(RF98_IMPLICIT_HEADER_MODE_ON_MASK); + spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t); + eeprom_write_byte(CFG_MC1_IDX, t); + /* set DetectOptimize */ + t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & + ~(RF98_DETOPTIMIZE_MASK); + t |= RF98_DETOPTIMIZE_03; + spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t); + eeprom_write_byte(CFG_DEO_IDX, t); + /* set DetectionThreshold */ + spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, + RF98_DETTH_0A); + eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0A); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_basic_cr_draw() +{ + uint8_t i; + + clearScreen(); + goHome(); + uart_println("Modem coding rate in symbols:"); + uart_println(""); + for (i=0;i> 1) & 0x07) == (i+1) ) + uart_putchar('+'); + else + uart_putchar(' '); + uart_putchar(i+'1'); + uart_puts(" - "); + uart_println(cr_levels[i]); + } + uart_println(" [ENTER] - Return to the previous menue"); +} + +static void menue_basic_cr() +{ + uint8_t t; + + do + { + menue_basic_cr_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + if ( (input >= '1') && (input < ('1' + CR_SZ)) ) + { + t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & 0xF1; + t |= ( ((uint8_t)(input - '1' + 1)) << 1 ); // CR starting with 1 + spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t); + eeprom_write_byte(CFG_MC1_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_basic_bw_draw() +{ + uint8_t i; + + clearScreen(); + goHome(); + uart_println("Modulation bandwidth:"); + uart_println(""); + for (i=0;i> 4) & 0x0F) == i ) + uart_putchar('+'); + else + uart_putchar(' '); + uart_putchar(i+'0'); + uart_puts(" - "); + uart_println(bw_levels[i]); + } + uart_println(" [ENTER] - Return to the previous menue"); +} + +static void menue_basic_bw() +{ + uint8_t t; + + do + { + menue_basic_bw_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + if ( (input >= '0') && (input < ('0' + BW_SZ)) ) + { + t = spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & 0x0F; + t |= ( ((uint8_t)(input - '0')) << 4 ); // BW starting with 0 + spi_writereg(RF98_REG_1D_MODEM_CONFIG1, t); + eeprom_write_byte(CFG_MC1_IDX, t); + if ( input == '9' ) + { + /* + For 500 kHz bandwidth we need to apply some corrections + to shadow registers. See SX127x Errata Note, Section 2.1. + */ + spi_writereg(RF98_REG_36, RF98_REG_36_500KHZ); + eeprom_write_byte(CFG_R36_IDX, RF98_REG_36_500KHZ); + spi_writereg(RF98_REG_3A, RF98_REG_3A_500KHZ); + eeprom_write_byte(CFG_R3A_IDX, RF98_REG_3A_500KHZ); + } else + { + spi_writereg(RF98_REG_36, RF98_REG_36_DEFAULT); + eeprom_write_byte(CFG_R36_IDX, RF98_REG_36_DEFAULT); + //spi_writereg(RF98_REG_3A, RF98_REG_3A_DEFAULT); // not needed as per errata + //eeprom_write_byte(CFG_R3A_IDX, RF98_REG_3A_DEFAULT); + } + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_basic_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_println("Basic settings:"); + uart_println(""); + uart_puts("0 - TNC operation mode: "); + t = (cfg[CFG_TNC_IDX] & CFG_TNC_MOD_MASK) >> 3; + switch ( t ) + { + case MODE_BRIDGE: + uart_println("MODE_BRIDGE"); + break; + case MODE_KISS: + uart_println("MODE_KISS"); + break; + default: + uart_println("unknown"); + } + uart_puts("1 - TNC serial port speed: "); + t = cfg[CFG_TNC_IDX] & CFG_TNC_SSP_MASK; + if ( t < SIO_SP_SZ ) + uart_println(sio_speeds[t]); + else + uart_println("unknown"); + uart_puts("2 - TxDelay: "); + itoa_simple(dig, TXDELAY); + uart_puts(dig); + uart_println(" x 10 ms"); + uart_puts("3 - P-parameter: "); + itoa_simple(dig, P_PARAMETER); + uart_println(dig); + uart_puts("4 - SlotTime: "); + itoa_simple(dig, SLOTTIME); + uart_puts(dig); + uart_println(" x 10 ms"); + uart_puts("5 - Modem frequency: "); + itoa_simple(dig, (uint32_t)(rf_getfreq() * RF_STEP / 1000.0)); + uart_puts(dig); + uart_println(" kHz"); + uart_puts("6 - Modem spreading factor: "); + t = spi_readreg(RF98_REG_1E_MODEM_CONFIG2); + t &= RF98_SPREADING_FACTOR; + t >>= 4; + t -= 6; + //t = ((spi_readreg(RF98_REG_1E_MODEM_CONFIG2) & RF98_SPREADING_FACTOR) >> 4) - 6; + if ( t < SF_SZ ) + uart_println(sf_levels[t]); + else + uart_println("unknown"); + uart_puts("7 - Modem coding rate: "); + t = ((spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & RF98_CODING_RATE) >> 1) - 1; + if ( t < CR_SZ ) + uart_println(cr_levels[t]); + else + uart_println("unknown"); + uart_puts("8 - Modulation bandwidth: "); + t = (spi_readreg(RF98_REG_1D_MODEM_CONFIG1) & RF98_BW) >> 4; + if ( t < BW_SZ ) + uart_println(bw_levels[t]); + else + uart_println("unknown"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_basic_txdelay_draw() +{ + clearScreen(); + goHome(); + uart_println("TxDelay:"); + uart_println(""); + uart_puts("Current TxDelay (decimal): "); + itoa_simple(dig, TXDELAY); + uart_println(dig); + uart_puts("Enter TxDelay in range 0..255 or hit [ENTER] to return: "); +} + +static void menue_basic_txdelay() +{ + do + { + menue_basic_txdelay_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + eeprom_write_byte(CFG_TXD_IDX, (uint8_t)atoi_simple(dig)); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_basic_ppar_draw() +{ + clearScreen(); + goHome(); + uart_println("P-parameter:"); + uart_println("Note that p = 1 (P = 255) means 'transmit as soon as the channel clears.'"); + uart_println("The default value is P = 63 (i.e., p = 0.25)."); + uart_println(""); + uart_puts("Current P-parameter (decimal): "); + itoa_simple(dig, P_PARAMETER); + uart_println(dig); + uart_puts("Enter P-parameter in range 0..255 or hit [ENTER] to return: "); +} + +static void menue_basic_ppar() +{ + do + { + menue_basic_ppar_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + eeprom_write_byte(CFG_PPR_IDX, (uint8_t)atoi_simple(dig)); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_basic_slottime_draw() +{ + clearScreen(); + goHome(); + uart_println("SlotTime:"); + uart_println(""); + uart_puts("Current SlotTime (decimal): "); + itoa_simple(dig, SLOTTIME); + uart_println(dig); + uart_puts("Enter SlotTime in range 0..255 or hit [ENTER] to return: "); +} + +static void menue_basic_slottime() +{ + do + { + menue_basic_slottime_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + eeprom_write_byte(CFG_SLT_IDX, (uint8_t)atoi_simple(dig)); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_basic() +{ + do + { + menue_basic_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + menue_basic_mode(); + break; + case '1': + menue_basic_serial(); + break; + case '2': + menue_basic_txdelay(); + break; + case '3': + menue_basic_ppar(); + break; + case '4': + menue_basic_slottime(); + break; + case '5': + menue_basic_freq(); + break; + case '6': + menue_basic_sf(); + break; + case '7': + menue_basic_cr(); + break; + case '8': + menue_basic_bw(); + break; + case '\r': + continue; + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_readmodem_draw() +{ + clearScreen(); + goHome(); + uart_println("Populate TNC EEPROM data by reading modem registers"); + uart_println("(This will overwrite the previously stored settings in TNC EEPROM.)"); + uart_println(""); + uart_println("1 - Overwrite TNC EEPROM settings with modem data"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_readmodem() +{ + bool pressed = false; + bool m_read = false; + + do + { + menue_readmodem_draw(); + if ( pressed ) + { + pressed = false; + if ( m_read ) + uart_println("\r\nOperation completed"); + else + uart_println("\r\nNo action performed"); + } + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + pressed = true; + input = uart_getc(); + if ( input == '1' ) + { + eeprom_populate(); + m_read = true; + } else { + m_read = false; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_draw() +{ + clearScreen(); + goHome(); + uart_println("Advanced settings (see datasheet for more info):"); + uart_println(""); + uart_println("0 - PaConfig"); + uart_println("1 - PaRamp"); + uart_println("2 - Ocp"); + uart_println("3 - Lna"); + uart_println("4 - Preamble"); + uart_println("5 - LowDatarateOptimize"); + uart_println("6 - AgcAutoOn"); + uart_println("7 - PpmCorrection"); + uart_println("8 - DetectOptimize"); + uart_println("9 - InvertIq"); + uart_println("a - DetectionThreshold"); + uart_println("b - SyncWord"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paconfig_draw() +{ + clearScreen(); + goHome(); + uart_puts("RegPaConfig (0x09): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG)); + uart_println(dig); + uart_println(""); + uart_puts("1 - PaSelect: "); + if ( spi_readreg(RF98_REG_09_PA_CONFIG) >> 7 ) + uart_println("PA_BOOST"); + else + uart_println("RFO"); + uart_puts("2 - MaxPower: "); + itoa_simple(dig, (spi_readreg(RF98_REG_09_PA_CONFIG) & 0x70) >> 4); + uart_println(dig); + uart_puts("3 - OutputPower: "); + itoa_simple(dig, spi_readreg(RF98_REG_09_PA_CONFIG) & 0x0F); + uart_println(dig); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paconfig_paselect_draw() +{ + uint8_t t; + clearScreen(); + goHome(); + uart_println("RegPaConfig PaSelect:"); + uart_println(""); + t = spi_readreg(RF98_REG_09_PA_CONFIG) >> 7; + t ? uart_puts(" ") : uart_puts("+"); + uart_println("0 - RFO"); + t ? uart_puts("+") : uart_puts(" "); + uart_println("1 - PA_BOOST"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paconfig_paselect() +{ + uint8_t t; + + do + { + menue_adv_paconfig_paselect_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_PA_SELECT); + spi_writereg(RF98_REG_09_PA_CONFIG, t); + eeprom_write_byte(CFG_PAC_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_PA_SELECT); + t |= RF98_PA_SELECT; + spi_writereg(RF98_REG_09_PA_CONFIG, t); + eeprom_write_byte(CFG_PAC_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_paconfig_mpower_draw() +{ + uint8_t i, t; + clearScreen(); + goHome(); + uart_println("RegPaConfig MaxPower:"); + uart_println(""); + t = ( spi_readreg(RF98_REG_09_PA_CONFIG) & RF98_MAX_POWER ) >> 4; + for (i = 0; i <= 7; i++) { + itoa_simple(dig, i); + (t == i) ? uart_puts("+") : uart_puts(" "); + uart_puts(dig); + uart_puts(" - "); + uart_println(dig); + } + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paconfig_mpower() +{ + uint8_t t; + + do + { + menue_adv_paconfig_mpower_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + if ( (input >= '0') && (input <= '7') ) + { + t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_MAX_POWER); + t |= ( input - '0' ) << 4; + spi_writereg(RF98_REG_09_PA_CONFIG, t); + eeprom_write_byte(CFG_PAC_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_paconfig_opower_draw() +{ + uint8_t i, t; + + clearScreen(); + goHome(); + uart_println("RegPaConfig OutputPower:"); + uart_println(""); + t = spi_readreg(RF98_REG_09_PA_CONFIG) & RF98_OUTPUT_POWER; + for (i = 0; i <= 15; i++) { + (t == i) ? uart_puts("+") : uart_puts(" "); + itoa_simple(dig, i); + if ( i <=9 ) + { + uart_puts(dig); + } else { + switch ( i ) + { + case 10: + uart_puts("a"); + break; + case 11: + uart_puts("b"); + break; + case 12: + uart_puts("c"); + break; + case 13: + uart_puts("d"); + break; + case 14: + uart_puts("e"); + break; + case 15: + uart_puts("f"); + break; + } + } + uart_puts(" - "); + uart_println(dig); + } + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paconfig_opower() +{ + uint8_t t; + + do + { + menue_adv_paconfig_opower_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + if ( (input >= '0') && (input <= '9') || (input >= 'a') && (input <= 'f') ) + { + t = spi_readreg(RF98_REG_09_PA_CONFIG) & ~(RF98_OUTPUT_POWER); + if ( (input >= '0') && (input <= '9') ) + t |= ( input - '0' ); + else + t |= ( input - 'a' + 10 ); + spi_writereg(RF98_REG_09_PA_CONFIG, t); + eeprom_write_byte(CFG_PAC_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_paconfig() +{ + do + { + menue_adv_paconfig_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '1': + menue_adv_paconfig_paselect(); + break; + case '2': + menue_adv_paconfig_mpower(); + break; + case '3': + menue_adv_paconfig_opower(); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_paramp_draw() +{ + uint8_t i, t; + + clearScreen(); + goHome(); + uart_puts("RegPaRamp (0x0A): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_0A_PA_RAMP)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_0A_PA_RAMP)); + uart_println(dig); + uart_println(""); + t = spi_readreg(RF98_REG_0A_PA_RAMP) & RF98_PA_RAMP; + for (i = 0; i <= 15; i++) { + (t == i) ? uart_puts("+") : uart_puts(" "); + itoa_simple(dig, i); + if ( i <=9 ) + { + uart_puts(dig); + } else { + switch ( i ) + { + case 10: + uart_puts("a"); + break; + case 11: + uart_puts("b"); + break; + case 12: + uart_puts("c"); + break; + case 13: + uart_puts("d"); + break; + case 14: + uart_puts("e"); + break; + case 15: + uart_puts("f"); + break; + } + } + uart_puts(" - "); + uart_println(ramp_levels[i]); + } + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_paramp() +{ + uint8_t t; + + do + { + menue_adv_paramp_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + if ( (input >= '0') && (input <= '9') || (input >= 'a') && (input <= 'f') ) + { + t = spi_readreg(RF98_REG_0A_PA_RAMP) & ~(RF98_PA_RAMP); + if ( (input >= '0') && (input <= '9') ) + t |= ( input - '0' ); + else + t |= ( input - 'a' + 10 ); + spi_writereg(RF98_REG_0A_PA_RAMP, t); + eeprom_write_byte(CFG_PAR_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_ocp_draw() +{ + clearScreen(); + goHome(); + uart_puts("RegOcp (0x0B): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_0B_OCP)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_0B_OCP)); + uart_println(dig); + uart_println(""); + uart_puts("1 - OcpOn: "); + if ( (spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_ON ) >> 5 ) + uart_println("OCP enabled"); + else + uart_println("OCP disabled"); + uart_puts("2 - OcpTrim: "); + itoa_simple(dig, (spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_TRIM) ); + uart_println(dig); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_ocp_ocpon_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_println("RegOcp OcpOn:"); + uart_println(""); + t = ( spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_ON ) >> 5; + t ? uart_puts(" ") : uart_puts("+"); + uart_println("0 - OCP disabled"); + t ? uart_puts("+") : uart_puts(" "); + uart_println("1 - OCP enabled"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_ocp_ocpon() +{ + uint8_t t; + + do + { + menue_adv_ocp_ocpon_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_ON); + spi_writereg(RF98_REG_0B_OCP, t); + eeprom_write_byte(CFG_OCP_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_ON); + t |= RF98_OCP_ON; + spi_writereg(RF98_REG_0B_OCP, t); + eeprom_write_byte(CFG_OCP_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_ocp_ocptrim_draw() +{ + clearScreen(); + goHome(); + uart_println("OcpTrim:"); + uart_println(""); + uart_puts("Current OcpTrim: "); + itoa_simple(dig, spi_readreg(RF98_REG_0B_OCP) & RF98_OCP_TRIM); + uart_println(dig); + uart_puts("Enter OcpTrim in range 0..31 or hit [ENTER] to return: "); +} + +static void menue_adv_ocp_ocptrim() +{ + uint8_t t; + + do + { + menue_adv_ocp_ocptrim_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + t = spi_readreg(RF98_REG_0B_OCP) & ~(RF98_OCP_TRIM); + t |= (uint8_t)(atoi_simple(dig) % 32); + eeprom_write_byte(CFG_OCP_IDX, t); + spi_writereg(RF98_REG_0B_OCP, t); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_adv_ocp() +{ + do + { + menue_adv_ocp_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '1': + menue_adv_ocp_ocpon(); + break; + case '2': + menue_adv_ocp_ocptrim(); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + + +static void menue_adv_lna_draw() +{ + clearScreen(); + goHome(); + uart_puts("RegLna (0x0C): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_0C_LNA)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_0C_LNA)); + uart_println(dig); + uart_println(""); + uart_puts("1 - LnaGain: "); + itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_GAIN)); + uart_println(dig); + uart_puts("2 - LnaBoostLf: "); + itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_BOOST_LF)); + uart_println(dig); + uart_puts("3 - LnaBoostHf: "); + itoa_simple(dig, BFN_GET(spi_readreg(RF98_REG_0C_LNA), RF98_LNA_BOOST_HF)); + uart_println(dig); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_lna_lnagain_draw() +{ + uint8_t i; + + clearScreen(); + goHome(); + uart_println("RegLna LnaGain:"); + uart_println(""); + for (i=0;i= '1') && (input <= '6') ) + { + t = spi_readreg(RF98_REG_0C_LNA) & ~(RF98_LNA_GAIN_MASK); + switch ( input ) + { + case '1': + t |= RF98_LNA_GAIN_G1; + break; + case '2': + t |= RF98_LNA_GAIN_G2; + break; + case '3': + t |= RF98_LNA_GAIN_G3; + break; + case '4': + t |= RF98_LNA_GAIN_G4; + break; + case '5': + t |= RF98_LNA_GAIN_G5; + break; + case '6': + t |= RF98_LNA_GAIN_G6; + break; + } + spi_writereg(RF98_REG_0C_LNA, t); + eeprom_write_byte(CFG_LNA_IDX, t); + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_lna_lnabhf_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_println("RegLna LnaBoostHf:"); + uart_println(""); + t = spi_readreg(RF98_REG_0C_LNA) & RF98_LNA_BOOST_HF_MASK; + t == RF98_LNA_BOOST_HF_DEFAULT ? uart_puts("+") : uart_puts(" "); + uart_println("0 - Default LNA current"); + t == RF98_LNA_BOOST_HF_150PC ? uart_puts("+") : uart_puts(" "); + uart_println("3 - Boost on, 150% LNA current"); + uart_println(" [ENTER] - Return to the previous menue"); +} + +static void menue_adv_lna_lnabhf() +{ + uint8_t t; + + do + { + menue_adv_lna_lnabhf_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + t = spi_readreg(RF98_REG_0C_LNA) & ~(RF98_LNA_BOOST_HF_MASK); + switch ( input ) + { + case '0': + t |= RF98_LNA_BOOST_HF_DEFAULT; + spi_writereg(RF98_REG_0C_LNA, t); + eeprom_write_byte(CFG_LNA_IDX, t); + break; + case '3': + t |= RF98_LNA_BOOST_HF_150PC; + spi_writereg(RF98_REG_0C_LNA, t); + eeprom_write_byte(CFG_LNA_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_lna() +{ + do + { + menue_adv_lna_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + input = uart_getc(); + switch ( input ) { + case '1': + menue_adv_lna_lnagain(); + break; + case '2': + //menue_adv_lna_lnablf(); + break; + case '3': + menue_adv_lna_lnabhf(); + break; + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_preamble_draw() +{ + uint16_t t; + + clearScreen(); + goHome(); + uart_println("Preamble:"); + uart_puts(" RegPreambleMsb (0x20): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_20_PREAMBLE_MSB)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_20_PREAMBLE_MSB)); + uart_println(dig); + uart_puts(" RegPreambleLsb (0x21): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_21_PREAMBLE_LSB)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_21_PREAMBLE_LSB)); + uart_println(dig); + uart_println(""); + uart_puts("Current Preamble: "); + t = spi_readreg(RF98_REG_20_PREAMBLE_MSB) << 8; + t |= spi_readreg(RF98_REG_21_PREAMBLE_LSB); + itoa_simple(dig, t); + uart_println(dig); + uart_puts("Enter Preamble in range 0..65535 or hit [ENTER] to return: "); +} + +static void menue_adv_preamble() +{ + uint8_t t; + uint16_t m; + + do + { + menue_adv_preamble_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + m = (uint16_t)atoi_simple(dig); + t = HIBYTE(m); + spi_writereg(RF98_REG_20_PREAMBLE_MSB, t); + eeprom_write_byte(CFG_PRU_IDX, t); + t = LOBYTE(m); + spi_writereg(RF98_REG_21_PREAMBLE_LSB, t); + eeprom_write_byte(CFG_PRL_IDX, t); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_adv_lowdropt_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_println("LowDatarateOptimize:"); + uart_puts(" RegModemConfig3 (0x26): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3)); + uart_println(dig); + uart_println(""); + t = ( spi_readreg(RF98_REG_26_MODEM_CONFIG3) & RF98_LDOPTIMIZE ) >> 3; + t ? uart_puts(" ") : uart_puts("+"); + uart_println("0 - Disabled"); + t ? uart_puts("+") : uart_puts(" "); + uart_println("1 - Enabled; mandated for when the symbol length exceeds 16ms"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_lowdropt() +{ + uint8_t t; + + do + { + menue_adv_lowdropt_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_LDOPTIMIZE); + spi_writereg(RF98_REG_26_MODEM_CONFIG3, t); + eeprom_write_byte(CFG_MC3_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_LDOPTIMIZE); + t |= RF98_LDOPTIMIZE; + spi_writereg(RF98_REG_26_MODEM_CONFIG3, t); + eeprom_write_byte(CFG_MC3_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_agcaon_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_println("AgcAutoOn:"); + uart_puts(" RegModemConfig3 (0x26): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_26_MODEM_CONFIG3)); + uart_println(dig); + uart_println(""); + t = ( spi_readreg(RF98_REG_26_MODEM_CONFIG3) & RF98_AGCAUTOON ) >> 2; + t ? uart_puts(" ") : uart_puts("+"); + uart_println("0 - Disabled. LNA gain set by register LnaGain"); + t ? uart_puts("+") : uart_puts(" "); + uart_println("1 - Enabled. LNA gain set by the internal AGC loop"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_agcaon() +{ + uint8_t t; + + do + { + menue_adv_agcaon_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_AGCAUTOON); + spi_writereg(RF98_REG_26_MODEM_CONFIG3, t); + eeprom_write_byte(CFG_MC3_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_26_MODEM_CONFIG3) & ~(RF98_AGCAUTOON); + t |= RF98_AGCAUTOON; + spi_writereg(RF98_REG_26_MODEM_CONFIG3, t); + eeprom_write_byte(CFG_MC3_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_ppmcorr_draw() +{ + clearScreen(); + goHome(); + uart_println("RegPpmCorrection (0x27):"); + uart_println(""); + uart_puts("Current PpmCorrection: "); + itoa_simple(dig, spi_readreg(RF98_REG_27_PPM_CORRECTION)); + uart_println(dig); + uart_puts("Enter RegPpmCorrection in range 0..255 or hit [ENTER] to return: "); +} + +static void menue_adv_ppmcorr() +{ + uint8_t t; + + do + { + menue_adv_ppmcorr_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + t = (uint8_t)atoi_simple(dig); + spi_writereg(RF98_REG_27_PPM_CORRECTION, t); + eeprom_write_byte(CFG_PPM_IDX, t); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_adv_detectopt_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_puts("RegDetectOptimize (0x31): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_31_DETECT_OPTIMIZ)); + uart_println(dig); + uart_println(""); + t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & RF98_DETOPTIMIZE; + (t == RF98_DETOPTIMIZE_03) ? uart_puts("+") : uart_puts(" "); + uart_println("0 - 0x03 -> SF7 to SF12"); + (t == RF98_DETOPTIMIZE_05) ? uart_puts("+") : uart_puts(" "); + uart_println("1 - 0x05 -> SF6"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_detectopt() +{ + uint8_t t; + + do + { + menue_adv_detectopt_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & ~(RF98_DETOPTIMIZE); + t |= RF98_DETOPTIMIZE_03; + spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t); + eeprom_write_byte(CFG_DEO_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_31_DETECT_OPTIMIZ) & ~(RF98_DETOPTIMIZE); + t |= RF98_DETOPTIMIZE_05; + spi_writereg(RF98_REG_31_DETECT_OPTIMIZ, t); + eeprom_write_byte(CFG_DEO_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_invertiq_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_puts("RegInvertIQ (0x33): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_33_INVERT_IQ)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_33_INVERT_IQ)); + uart_println(dig); + uart_println(""); + t = spi_readreg(RF98_REG_33_INVERT_IQ) & RF98_INVERT_IQ; + t ? uart_puts(" ") : uart_puts("+"); + uart_println("0 - normal mode"); + t ? uart_puts("+") : uart_puts(" "); + uart_println("1 - I and Q signals are inverted"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_invertiq() +{ + uint8_t t; + + do + { + menue_adv_invertiq_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + t = spi_readreg(RF98_REG_33_INVERT_IQ) & ~(RF98_INVERT_IQ); + spi_writereg(RF98_REG_33_INVERT_IQ, t); + eeprom_write_byte(CFG_IIQ_IDX, t); + break; + case '1': + t = spi_readreg(RF98_REG_33_INVERT_IQ) & ~(RF98_INVERT_IQ); + t |= RF98_INVERT_IQ; + spi_writereg(RF98_REG_33_INVERT_IQ, t); + eeprom_write_byte(CFG_IIQ_IDX, t); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_detth_draw() +{ + uint8_t t; + + clearScreen(); + goHome(); + uart_puts("RegDetectionThreshold (0x37): 0x"); + itox_simple(dig, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD)); + uart_puts(dig); + uart_puts(" 0b"); + itob_simple(dig, spi_readreg(RF98_REG_37_DETECTION_THRESHOLD)); + uart_println(dig); + uart_println(""); + t = spi_readreg(RF98_REG_37_DETECTION_THRESHOLD); + (t == RF98_DETTH_0A) ? uart_puts("+") : uart_puts(" "); + uart_println("0 - 0x0A -> SF7 to SF12"); + (t == RF98_DETTH_0C) ? uart_puts("+") : uart_puts(" "); + uart_println("1 - 0x0C -> SF6"); + uart_println("[ENTER] - Return to the previous menue"); +} + +static void menue_adv_detth() +{ + do + { + menue_adv_detth_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + switch ( input ) { + case '0': + spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, RF98_DETTH_0A); + eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0A); + break; + case '1': + spi_writereg(RF98_REG_37_DETECTION_THRESHOLD, RF98_DETTH_0C); + eeprom_write_byte(CFG_DTT_IDX, RF98_DETTH_0C); + break; + } + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_adv_syncword_draw() +{ + clearScreen(); + goHome(); + uart_println("RegSyncWord (0x39):"); + uart_println(""); + uart_puts("Current SyncWord (decimal): "); + itoa_simple(dig, spi_readreg(RF98_REG_39_SYNC_WORD)); + uart_println(dig); + uart_puts("Enter SyncWord in range 0..255 or hit [ENTER] to return: "); +} + +static void menue_adv_syncword() +{ + uint8_t t; + + do + { + menue_adv_syncword_draw(); + uart_readln(dig, DIG_SZ); + if ( dig[0] != '\0' ) + { + t = (uint8_t)atoi_simple(dig); + spi_writereg(RF98_REG_39_SYNC_WORD, t); + eeprom_write_byte(CFG_SCW_IDX, t); + } + } while ( (flag.tnc_mode == MODE_SETUP) && (dig[0] != '\0') ); +} + +static void menue_adv() +{ + do + { + menue_adv_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) + input = uart_getc(); + switch ( input ) { + case '0': + menue_adv_paconfig(); + break; + case '1': + menue_adv_paramp(); + break; + case '2': + menue_adv_ocp(); + break; + case '3': + menue_adv_lna(); + break; + case '4': + menue_adv_preamble(); + break; + case '5': + menue_adv_lowdropt(); + break; + case '6': + menue_adv_agcaon(); + break; + case '7': + menue_adv_ppmcorr(); + break; + case '8': + menue_adv_detectopt(); + break; + case '9': + menue_adv_invertiq(); + break; + case 'a': + menue_adv_detth(); + break; + case 'b': + menue_adv_syncword(); + break; + } + } while ( (flag.tnc_mode == MODE_SETUP) && (input != '\r') ); + input = '\0'; +} + +static void menue_regdump() +{ + uint8_t n, t; + + clearScreen(); + goHome(); + + for (uint8_t i=0; i <= 15; i++) + { + for (uint8_t j=0;j<=3;j++) + { + n = j+i*4; + itox_simple(dig, n); + uart_puts(dig); + uart_puts(": "); + t = spi_readreg(n); + itox_simple(dig, t); + uart_puts(dig); + uart_putchar(' '); + itob_simple(dig, t); + uart_puts(dig); + uart_puts(" | "); + } + uart_puts("\r\n"); + } + uart_puts("\r\n"); + uart_puts("Press any key to continue"); + __wait_for_interrupt(); +} + +static void menue_draw() +{ + clearScreen(); + goHome(); + uart_println("KISS TNC for SX127x or RF(M)9x LoRa modems"); + uart_println(""); + uart_puts("Onboard Semtech chip id: 0x"); + itox_simple(dig, spi_readreg(RF98_REG_42_VERSION)); + uart_println(dig); + uart_println(""); + uart_println("Select TNC settings:"); + uart_println(""); + uart_println("1 - Basic settings"); + uart_println("2 - Advanced settings"); + uart_println("3 - Initialise TNC EEPROM"); + uart_println("4 - Dump SX127x hardware registers"); +} + +void menue() +{ + rf_setopmode(RF98_MODE_STDBY); + tim2_stop(); + uart_stop(); + uart_init(BD9600); + input = '\0'; + do + { + menue_draw(); + __wait_for_interrupt(); + if ( flag.uart_rxne ) { + input = uart_getc(); + switch ( input ) { + case '1': + menue_basic(); + break; + case '2': + menue_adv(); + break; + case '3': + menue_readmodem(); + break; + case '4': + menue_regdump(); + break; + } + } + } while (flag.tnc_mode == MODE_SETUP); +} diff --git a/menue.h b/menue.h new file mode 100644 index 0000000..5db6f50 --- /dev/null +++ b/menue.h @@ -0,0 +1,7 @@ +// menue.h +#ifndef MENUE_H +#define MENUE_H + +void menue(void); + +#endif diff --git a/rfm98w.c b/rfm98w.c new file mode 100644 index 0000000..c074c3f --- /dev/null +++ b/rfm98w.c @@ -0,0 +1,83 @@ +#include "config.h" +#include "rfm98w.h" +#include "spi.h" +#include "uart.h" + +void rf_setopmode(uint8_t opmode) +{ + spi_writereg(RF98_REG_01_OP_MODE, RF98_MODE_STDBY); + spi_writereg(RF98_REG_01_OP_MODE, opmode); +} + +void rf_setfreq(uint32_t freq) +{ + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_06_FRF_MSB, (uint8_t)(freq >> 16)); + spi_writereg(RF98_REG_07_FRF_MID, (uint8_t)(freq >> 8)); + spi_writereg(RF98_REG_08_FRF_LSB, (uint8_t)(freq)); +} + +uint32_t rf_getfreq() +{ + uint32_t frq; + + rf_setopmode(RF98_MODE_STDBY); + frq = (uint32_t)spi_readreg(RF98_REG_06_FRF_MSB) << 16; + frq |= (uint32_t)spi_readreg(RF98_REG_07_FRF_MID) << 8; + frq |= spi_readreg(RF98_REG_08_FRF_LSB); + + return frq; +} + +void rf_send(char *data, uint8_t len) +{ + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_12_IRQ_FLAGS, RF98_TX_DONE); /* clear interrupt */ + //spi_writereg(RF98_REG_12_IRQ_FLAGS, 0xFF); /* clear interrupt */ + spi_writereg(RF98_REG_0E_FIFO_TX_BASE_ADDR, RF_FIFO_TXBASE); + spi_writereg(RF98_REG_0D_FIFO_ADDR_PTR, RF_FIFO_TXBASE); + if ( flag.header_implicit ) + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, 0xFF); + else + spi_writereg(RF98_REG_22_PAYLOAD_LENGTH, len); + spi_writeburst(RF98_REG_00_FIFO, data, len); + TX_ON; + rf_setopmode(RF98_MODE_TX); + while ( ! (spi_readreg(RF98_REG_12_IRQ_FLAGS) & RF98_TX_DONE) ); // block +} + +uint8_t rf_recv(void) +{ + uint8_t i, num = 0; + + if ( !(spi_readreg(RF98_REG_12_IRQ_FLAGS) & + RF98_PAYLOAD_CRC_ERROR) ) + { + rf_setopmode(RF98_MODE_STDBY); + spi_writereg(RF98_REG_0D_FIFO_ADDR_PTR, spi_readreg(RF98_REG_10_FIFO_RX_CURRENT_ADDR)); + + /* In implicit mode 1st byte is packet length */ + if ( flag.header_implicit ) + { + num = spi_readreg(RF98_REG_00_FIFO); + } else + { + num = spi_readreg(RF98_REG_13_RX_NB_BYTES); + } + + for (i=0; i> 8) + (UART_DIV(baud) & 0x000F)); // set + USART1_BRR1 = (char)((UART_DIV(baud) & 0x0FF0) >> 4); // baudrate + + USART1_CR2_TEN = 1; // TX enable + USART1_CR2_REN = 1; // RX enable + USART1_CR2_RIEN = 1; // Enable RX interrupt +} + +void uart_stop(void) +{ + while (USART1_SR_TC == 0); // disable only after tx complete + USART1_CR2_TEN = 0; // TX disable + USART1_CR2_REN = 0; // RX disable + USART1_CR2_RIEN = 0; // Disable RX interrupt + CLK_PCKENR1_PCKEN15 = 0; + + // Configure PD6 (USART1_RX) for output + PA_DDR_DDR3 = 1; // out + PA_CR1_C13 = 0; // open drain PA3 + + PA_CR1_C12 = 0; // open drain PA2 +} + +void uart_putchar(char c) +{ + while (USART1_SR_TXE == 0); + USART1_DR = c; +} + +char uart_getchar(void) +{ + while(USART1_SR_RXNE == 0); // Wait until char is available + return (USART1_DR); // Get it +} + +char uart_getc(void) +{ + flag.uart_rxne = false; + return uart_dr; +} + +void uart_puts(const char *s) +{ + while (s && *s) + uart_putchar (*s++); +} + +void uart_putsn(const char *s, uint16_t len) +{ + if ( len == 0 ) return; + while (len--) + uart_putchar (*s++); +} + +void uart_println(const char *s) +{ + while (s && *s) + uart_putchar (*s++); + uart_puts("\r\n"); +} + +void uart_readln(char *buf, uint8_t sz) +{ + char input; + uint8_t n = 0; + + do + { + __wait_for_interrupt(); + if ( flag.uart_rxne ) + { + input = uart_getc(); + uart_putchar(input); + buf[n++] = input; + } + } while (input != '\r' && n < sz); + if ( n < sz ) + buf[n-1] = '\0'; + else + buf[sz-1] = '\0'; +} \ No newline at end of file diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..66a9f69 --- /dev/null +++ b/uart.h @@ -0,0 +1,30 @@ +// uart.h +#ifndef UART_H +#define UART_H + +#include "config.h" + +#define BD9600 0 +#define BD19200 1 +#define BD38400 2 +#define BD57600 3 +#define BD115200 4 +#define BD230400 5 +#define BD460800 6 +#define BD921600 7 + +#define UART_DIV(x) (int)((( F_MASTER * 1000000.0f ) / ( x ) ) + 0.5f ) + +extern char uart_dr; + +void uart_init(uint8_t baudspec); +void uart_stop(void); +void uart_putchar(char); +void uart_puts(const char *); +void uart_putsn(const char *, uint16_t); +void uart_println(const char *); +void uart_readln(char *, uint8_t); +char uart_getchar(void); +char uart_getc(void); + +#endif diff --git a/util.c b/util.c new file mode 100644 index 0000000..a3aca0e --- /dev/null +++ b/util.c @@ -0,0 +1,71 @@ +#include "config.h" +#include "util.h" + +uint8_t random() +{ + seed ^= seed << 13; + seed ^= seed >> 17; + seed ^= seed << 5; + return (uint8_t)(seed >> 8); +} + +void eeprom_write_byte(uint8_t idx, uint8_t val) +{ + if ( idx < CFG_SZ ) + { + FLASH_DUKR = 0xAE; + FLASH_DUKR = 0x56; + cfg[idx] = val; + while(!FLASH_IAPSR_EOP); + FLASH_IAPSR_DUL = 0; + } +} + +uint8_t assemble(uint8_t len, uint8_t id) +{ + uint8_t i, idx = 0; + + if ( flag.header_implicit ) + idx++; + if ( flag.rf_multiframe ) + idx++; + + if ( flag.header_implicit ) + rf_txbuf[0] = len; + + if ( flag.rf_multiframe ) + { + rf_txbuf[idx-1] = id; + } + + for(i = idx; i < len+idx; i++) + { + rf_txbuf[i] = cbuf_pop(); + } + + return len+idx; +} + +char cbuf_pop2(void) +{ + if ( CBUF_Len(utmpbuf) < TTBL_SZ ) + RTS_SET_LOW; + return CBUF_Pop(utmpbuf); +} + +char cbuf_pop(void) +{ + if ( CBUF_Len(urxcbuf) < TBL_SZ ) + RTS_SET_LOW; + return CBUF_Pop(urxcbuf); +} + +void cbuf_push(char c) +{ + /* Hardware flow control */ + if ( CBUF_Len(urxcbuf) >= TBH_SZ ) + { + RTS_SET_HIGH; + } + CBUF_Push(urxcbuf, c); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..53bf1c6 --- /dev/null +++ b/util.h @@ -0,0 +1,12 @@ +// util.h +#ifndef UTIL_H +#define UTIL_H + +uint8_t random(void); +uint8_t assemble(uint8_t len, uint8_t id); +char cbuf_pop2(void); +char cbuf_pop(void); +void cbuf_push(char c); +void eeprom_write_byte(uint8_t idx, uint8_t val); + +#endif \ No newline at end of file