MHVLib  20111227
An efficiency oriented runtime library for AVR microcontrollers
A:/eclipse/mhvlib/MHV_Shifter.h
Go to the documentation of this file.
00001 /* Copyright (c) 2011, Make, Hack, Void Inc
00002  * All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions are met:
00006  *  * Redistributions of source code must retain the above copyright
00007  *    notice, this list of conditions and the following disclaimer.
00008  *  * Redistributions in binary form must reproduce the above copyright
00009  *    notice, this list of conditions and the following disclaimer in the
00010  *    documentation and/or other materials provided with the distribution.
00011  *  * Neither the name of the Make, Hack, Void nor the
00012  *    names of its contributors may be used to endorse or promote products
00013  *    derived from this software without specific prior written permission.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00016  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00018  * DISCLAIMED. IN NO EVENT SHALL MAKE, HACK, VOID BE LIABLE FOR ANY
00019  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00020  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00021  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00022  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00023  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00024  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00025  */
00026 
00027 #ifndef MHV_SHIFTER_H_
00028 #define MHV_SHIFTER_H_
00029 
00030 #include <MHV_io.h>
00031 
00032 #ifndef MHV_CORE
00033 #ifndef MHV_SHIFT_WRITECLOCK
00034 #warning MHV_SHIFT_WRITECLOCK not defined - do not expect MHV_SHIFTOUT_* macros to work!
00035 #endif
00036 
00037 #ifndef MHV_SHIFT_WRITEDATA
00038 #warning MHV_SHIFT_WRITEDATA not defined - do not expect MHV_SHIFTOUT_* macros to work!
00039 #endif
00040 #endif
00041 
00042 #ifndef MHV_SHIFT_DELAY
00043 #define MHV_SHIFT_DELAY
00044 #endif
00045 
00046 #ifdef MHV_SHIFT_ORDER_MSB
00047 #define SHIFTOUT_BYTE_LOOP (mhv_shift_i = 7; mhv_shift_i >= 0; mhv_shift_i--)
00048 #define MHV_BIT_1 _BV(7)
00049 #define MHV_BIT_2 _BV(6)
00050 #define MHV_BIT_3 _BV(5)
00051 #define MHV_BIT_4 _BV(4)
00052 #define MHV_BIT_5 _BV(3)
00053 #define MHV_BIT_6 _BV(2)
00054 #define MHV_BIT_7 _BV(1)
00055 #define MHV_BIT_8 _BV(0)
00056 #else
00057 #define SHIFTOUT_BYTE_LOOP (mhv_shift_i = 0; mhv_shift_i < 8; mhv_shift_i++)
00058 #define MHV_BIT_1 _BV(0)
00059 #define MHV_BIT_2 _BV(1)
00060 #define MHV_BIT_3 _BV(2)
00061 #define MHV_BIT_4 _BV(3)
00062 #define MHV_BIT_5 _BV(4)
00063 #define MHV_BIT_6 _BV(5)
00064 #define MHV_BIT_7 _BV(6)
00065 #define MHV_BIT_8 _BV(7)
00066 #endif
00067 
00068 #if defined(MHV_SHIFT_WRITECLOCK) && defined(MHV_SHIFT_WRITEDATA)
00069 /* Shift a byte out, works both for devices that read on rising clock
00070  * & falling clock
00071  */
00072 
00073 #define MHV_SHIFTOUT_BYTE(data) \
00074 do { \
00075         int8_t          mhv_shift_i; \
00076         for SHIFTOUT_BYTE_LOOP { \
00077                 if ((data >> mhv_shift_i) & 0x01) { \
00078                         mhv_pinOn(MHV_SHIFT_WRITEDATA); \
00079                 } else { \
00080                         mhv_pinOff(MHV_SHIFT_WRITEDATA); \
00081                 } \
00082                 mhv_pinOn(MHV_SHIFT_WRITECLOCK); \
00083                 mhv_pinOff(MHV_SHIFT_WRITECLOCK); \
00084         } \
00085 } while (0);
00086 
00087 /* Write an array of data as clocked serial, where the reading device
00088  * reads data when the clock rises
00089  * Clock and data must be on the same port.
00090  * Other pins on this port should not be manipulated by interrupts or
00091  * this macro will corrupt them.
00092  * data & dataLength are sacrificial, upon completion, data will point to the
00093  * end of the array, and dataLength will be 0
00094  * mhv_dataCopy is used to avoid pointer indirection on each bit test
00095  *
00096  * @param       data            a pointer to the data to write
00097  * @param       dataLength      the length of the data (must be a variable)
00098  */
00099 #define MHV_SHIFTOUT_ARRAY_CLOCKED_RISING(data,len) \
00100 do { \
00101         uint8_t dataOffClockOff = *mhv_out(MHV_SHIFT_WRITEDATA) & \
00102                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA))) & ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK))); \
00103         uint8_t dataOffClockOn = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00104                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA)))) | _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)); \
00105         uint8_t dataOnClockOff = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00106                         ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK)))) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00107         uint8_t dataOnClockOn = *mhv_out(MHV_SHIFT_WRITEDATA) | \
00108                 _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00109         uint8_t mhv_dataCopy; \
00110  \
00111         while (len--) { \
00112                 mhv_dataCopy = *data++; \
00113                 if ((mhv_dataCopy & MHV_BIT_1)) { \
00114                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00115                         MHV_SHIFT_DELAY; \
00116                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00117                         MHV_SHIFT_DELAY; \
00118                 } else { \
00119                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00120                         MHV_SHIFT_DELAY; \
00121                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00122                         MHV_SHIFT_DELAY; \
00123                 } \
00124                 if ((mhv_dataCopy & MHV_BIT_2)) { \
00125                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00126                         MHV_SHIFT_DELAY; \
00127                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00128                         MHV_SHIFT_DELAY; \
00129                 } else { \
00130                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00131                         MHV_SHIFT_DELAY; \
00132                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00133                         MHV_SHIFT_DELAY; \
00134                 } \
00135                 if ((mhv_dataCopy & MHV_BIT_3)) { \
00136                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00137                         MHV_SHIFT_DELAY; \
00138                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00139                         MHV_SHIFT_DELAY; \
00140                 } else { \
00141                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00142                         MHV_SHIFT_DELAY; \
00143                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00144                         MHV_SHIFT_DELAY; \
00145                 } \
00146                 if ((mhv_dataCopy & MHV_BIT_4)) { \
00147                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00148                         MHV_SHIFT_DELAY; \
00149                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00150                         MHV_SHIFT_DELAY; \
00151                 } else { \
00152                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00153                         MHV_SHIFT_DELAY; \
00154                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00155                         MHV_SHIFT_DELAY; \
00156                 } \
00157                 if ((mhv_dataCopy & MHV_BIT_5)) { \
00158                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00159                         MHV_SHIFT_DELAY; \
00160                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00161                         MHV_SHIFT_DELAY; \
00162                 } else { \
00163                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00164                         MHV_SHIFT_DELAY; \
00165                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00166                         MHV_SHIFT_DELAY; \
00167                 } \
00168                 if ((mhv_dataCopy & MHV_BIT_6)) { \
00169                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00170                         MHV_SHIFT_DELAY; \
00171                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00172                         MHV_SHIFT_DELAY; \
00173                 } else { \
00174                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00175                         MHV_SHIFT_DELAY; \
00176                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00177                         MHV_SHIFT_DELAY; \
00178                 } \
00179                 if ((mhv_dataCopy & MHV_BIT_7)) { \
00180                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00181                         MHV_SHIFT_DELAY; \
00182                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00183                         MHV_SHIFT_DELAY; \
00184                 } else { \
00185                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00186                         MHV_SHIFT_DELAY; \
00187                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00188                         MHV_SHIFT_DELAY; \
00189                 } \
00190                 if ((mhv_dataCopy & MHV_BIT_8)) { \
00191                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00192                         MHV_SHIFT_DELAY; \
00193                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00194                         MHV_SHIFT_DELAY; \
00195                 } else { \
00196                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00197                         MHV_SHIFT_DELAY; \
00198                         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00199                         MHV_SHIFT_DELAY; \
00200                 } \
00201         }  \
00202         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00203 } while (0);
00204 
00205 /* Write an array of data as clocked serial, where the reading device
00206  * reads data when the clock rises
00207  * Clock and data must be on the same port.
00208  * Other pins on this port should not be manipulated by interrupts or
00209  * this macro will corrupt them.
00210  * data & dataLength are sacrificial, upon completion, data will point to the
00211  * end of the array, and dataLength will be 0
00212  * mhv_dataCopy is used to avoid pointer indirection on each bit test
00213  *
00214  * @param       data            a pointer to the data to write
00215  * @param       dataLength      the length of a element of data
00216  * @param       elements        the number of elements
00217  */
00218 #define MHV_SHIFTOUT_ELEMENTS_CLOCKED_RISING(data,len,elements) \
00219 do { \
00220         uint8_t dataOffClockOff = *mhv_out(MHV_SHIFT_WRITEDATA) & \
00221                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA))) & ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK))); \
00222         uint8_t dataOffClockOn = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00223                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA)))) | _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)); \
00224         uint8_t dataOnClockOff = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00225                         ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK)))) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00226         uint8_t dataOnClockOn = *mhv_out(MHV_SHIFT_WRITEDATA) | \
00227                 _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00228         uint8_t mhv_dataCopy; \
00229         uint8_t lenCopy; \
00230         while (elements--) { \
00231                 for (lenCopy = len; --lenCopy;) { \
00232                         mhv_dataCopy = *data++; \
00233                         if ((mhv_dataCopy & MHV_BIT_1)) { \
00234                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00235                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00236                         } else { \
00237                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00238                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00239                         } \
00240                         if ((mhv_dataCopy & MHV_BIT_2)) { \
00241                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00242                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00243                         } else { \
00244                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00245                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00246                         } \
00247                         if ((mhv_dataCopy & MHV_BIT_3)) { \
00248                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00249                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00250                         } else { \
00251                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00252                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00253                         } \
00254                         if ((mhv_dataCopy & MHV_BIT_4)) { \
00255                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00256                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00257                         } else { \
00258                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00259                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00260                         } \
00261                         if ((mhv_dataCopy & MHV_BIT_5)) { \
00262                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00263                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00264                         } else { \
00265                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00266                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00267                         } \
00268                         if ((mhv_dataCopy & MHV_BIT_6)) { \
00269                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00270                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00271                         } else { \
00272                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00273                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00274                         } \
00275                         if ((mhv_dataCopy & MHV_BIT_7)) { \
00276                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00277                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00278                         } else { \
00279                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00280                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00281                         } \
00282                         if ((mhv_dataCopy & MHV_BIT_8)) { \
00283                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00284                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00285                         } else { \
00286                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00287                                 *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00288                         } \
00289                 } \
00290         } \
00291         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00292 } while (0);
00293 
00294 
00295 /* Write an array of data as clocked serial, where the reading device
00296  * reads data when the clock falls
00297  * Clock and data must be on the same port.
00298  * Other pins on this port should not be manipulated by interrupts or
00299  * this macro will corrupt them.
00300  * data & dataLength are sacrificial, upon completion, data will point to the
00301  * end of the array, and dataLength will be 0
00302  * mhv_dataCopy is used to avoid pointer indirection on each bit test
00303  *
00304  * @param       data            a pointer to the data to write
00305  * @param       dataLength      the length of a element of data
00306  * @param       elements        the number of elements
00307  *
00308  */
00309 #define MHV_SHIFTOUT_ELEMENTS_CLOCKED_FALLING(data,len,elements) \
00310 do { \
00311         uint8_t dataOffClockOff = *mhv_out(MHV_SHIFT_WRITEDATA) & \
00312                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA))) & ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK))); \
00313         uint8_t dataOffClockOn = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00314                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA)))) | _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)); \
00315         uint8_t dataOnClockOff = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00316                         ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK)))) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00317         uint8_t dataOnClockOn = *mhv_out(MHV_SHIFT_WRITEDATA) | \
00318                 _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA));\
00319         uint8_t mhv_dataCopy; \
00320         uint8_t lenCopy; \
00321         while (elements--) { \
00322                 for (lenCopy = len; --lenCopy;) { \
00323                         mhv_dataCopy = *data++;\
00324                         if ((mhv_dataCopy & MHV_BIT_1)) {\
00325                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00326                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00327                         } else { \
00328                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00329                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00330                         } \
00331                         if ((mhv_dataCopy & MHV_BIT_2)) { \
00332                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00333                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00334                         } else { \
00335                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00336                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00337                         } \
00338                         if ((mhv_dataCopy & MHV_BIT_3)) { \
00339                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00340                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00341                         } else { \
00342                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00343                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00344                         } \
00345                         if ((mhv_dataCopy & MHV_BIT_4)) { \
00346                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00347                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00348                         } else { \
00349                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00350                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00351                         } \
00352                         if ((mhv_dataCopy & MHV_BIT_5)) { \
00353                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00354                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00355                         } else { \
00356                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00357                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00358                         } \
00359                         if ((mhv_dataCopy & MHV_BIT_6)) { \
00360                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00361                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00362                         } else { \
00363                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00364                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00365                         } \
00366                         if ((mhv_dataCopy & MHV_BIT_7)) { \
00367                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00368                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00369                         } else { \
00370                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00371                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00372                         } \
00373                         if ((mhv_dataCopy & MHV_BIT_8)) { \
00374                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00375                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00376                         } else { \
00377                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00378                                 mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00379                         } \
00380                 } \
00381         } \
00382         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00383 } while (0);
00384 
00385 
00386 /* Write an array of data as clocked serial, where the reading device
00387  * reads data when the clock falls
00388  * Clock and data must be on the same port.
00389  * Other pins on this port should not be manipulated by interrupts or
00390  * this macro will corrupt them.
00391  * data & dataLength are sacrificial, upon completion, data will point to the
00392  * end of the array, and dataLength will be 0
00393  * mhv_dataCopy is used to avoid pointer indirection on each bit test
00394  *
00395  * param        data            a pointer to the data to write
00396  * param        dataLength      the length of the data (must be a variable)
00397  *
00398  */
00399 #define MHV_SHIFTOUT_ARRAY_CLOCKED_FALLING(data,len) \
00400 do { \
00401         uint8_t dataOffClockOff = *mhv_out(MHV_SHIFT_WRITEDATA) & \
00402                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA))) & ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK))); \
00403         uint8_t dataOffClockOn = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00404                 ~(_BV(mhv_bit(MHV_SHIFT_WRITEDATA)))) | _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)); \
00405         uint8_t dataOnClockOff = (*mhv_out(MHV_SHIFT_WRITEDATA) & \
00406                         ~(_BV(mhv_bit(MHV_SHIFT_WRITECLOCK)))) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA)); \
00407         uint8_t dataOnClockOn = *mhv_out(MHV_SHIFT_WRITEDATA) | \
00408                 _BV(mhv_bit(MHV_SHIFT_WRITECLOCK)) | _BV(mhv_bit(MHV_SHIFT_WRITEDATA));\
00409         uint8_t mhv_dataCopy; \
00410 \
00411         while (!dataLength--) { \
00412                 mhv_dataCopy = *data++;\
00413                 if ((mhv_dataCopy & MHV_BIT_1)) {\
00414                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00415                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00416                 } else { \
00417                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00418                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00419                 } \
00420                 if ((mhv_dataCopy & MHV_BIT_2)) { \
00421                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00422                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00423                 } else { \
00424                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00425                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00426                 } \
00427                 if ((mhv_dataCopy & MHV_BIT_3)) { \
00428                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00429                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00430                 } else { \
00431                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00432                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00433                 } \
00434                 if ((mhv_dataCopy & MHV_BIT_4)) { \
00435                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00436                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00437                 } else { \
00438                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00439                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00440                 } \
00441                 if ((mhv_dataCopy & MHV_BIT_5)) { \
00442                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00443                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00444                 } else { \
00445                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00446                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00447                 } \
00448                 if ((mhv_dataCopy & MHV_BIT_6)) { \
00449                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00450                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00451                 } else { \
00452                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00453                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00454                 } \
00455                 if ((mhv_dataCopy & MHV_BIT_7)) { \
00456                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00457                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00458                 } else { \
00459                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00460                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00461                 } \
00462                 if ((mhv_dataCopy & MHV_BIT_8)) { \
00463                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOn; \
00464                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOnClockOff; \
00465                 } else { \
00466                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00467                         mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOff; \
00468                 } \
00469         } \
00470         *mhv_out(MHV_SHIFT_WRITEDATA) = dataOffClockOn; \
00471 } while (0);
00472 
00473 
00474 #endif
00475 
00476 /* Function variants of the above macros
00477  */
00478 void mhv_shiftout_byte_lsb(MHV_PIN *data, MHV_PIN *clock, uint8_t byte);
00479 void mhv_shiftout_byte_msb(MHV_PIN *data, MHV_PIN *clock, uint8_t byte);
00480 
00481 
00487 #define MHV_SHIFTER_CLOCKED_RISING_CREATE(name) \
00488         class MHV_Shifter_ ## name : public MHV_Shifter { \
00489                 public: \
00490                         void shiftOut(uint8_t *shiftData, uint8_t shiftLength); \
00491                         void shiftOut(uint8_t *shiftData, uint16_t shiftLength); \
00492                         void shiftOut(uint8_t *shiftData, uint8_t dataLength, uint16_t elements); \
00493         }; \
00494         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint8_t shiftLength) { \
00495                 MHV_SHIFTOUT_ARRAY_CLOCKED_RISING(shiftData, shiftLength); \
00496         } \
00497         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint16_t shiftLength) { \
00498                 MHV_SHIFTOUT_ARRAY_CLOCKED_RISING(shiftData, shiftLength); \
00499         } \
00500         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint8_t dataLength, uint16_t elements) { \
00501                 MHV_SHIFTOUT_ELEMENTS_CLOCKED_RISING(shiftData, dataLength, elements); \
00502         } \
00503         MHV_Shifter_ ## name name;
00504 
00510 #define MHV_SHIFTER_CLOCKED_FALLING_CREATE(name) \
00511         class MHV_Shifter_ ## name : public MHV_Shifter { \
00512                 public: \
00513                         void shiftOut(uint8_t *shiftData, uint8_t shiftLength); \
00514                         void shiftOut(uint8_t *shiftData, uint16_t shiftLength); \
00515                         void shiftOut(uint8_t *shiftData, uint8_t dataLength, uint16_t elements); \
00516         }; \
00517         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint8_t shiftLength) { \
00518                 MHV_SHIFTOUT_ARRAY_CLOCKED_FALLING(shiftData, shiftLength); \
00519         } \
00520         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint16_t shiftLength) { \
00521                 MHV_SHIFTOUT_ARRAY_CLOCKED_FALLING(shiftData, shiftLength); \
00522         } \
00523         void MHV_Shifter_ ## name::shiftOut(uint8_t *shiftData, uint8_t dataLength, uint16_t elements) { \
00524                 MHV_SHIFTOUT_ELEMENTS_CLOCKED_FALLING(shiftData, dataLength, elements); \
00525         } \
00526         MHV_Shifter_ ## name name;
00527 
00528 
00529 class MHV_Shifter {
00530 public:
00531         virtual void shiftOut(uint8_t *data, uint8_t length);
00532         virtual void shiftOut(uint8_t *data, uint16_t length);
00533         virtual void shiftOut(uint8_t *data, uint8_t length, uint16_t elements);
00534 };
00535 
00536 #endif /* MHV_SHIFTER_H_ */