spidigio.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 by egnite Software GmbH. 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
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log: spidigio.c,v $
00036  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00037  * Moved from dev.
00038  *
00039  * Revision 1.5  2005/02/02 19:59:12  haraldkipp
00040  * Typo corrected. Compiler failed, if ports were not configured. Due
00041  * to the completely broken port configuration this error wasn't
00042  * detected.
00043  *
00044  * Revision 1.4  2005/01/22 19:25:14  haraldkipp
00045  * Changed AVR port configuration names from PORTx to AVRPORTx.
00046  *
00047  * Revision 1.3  2004/09/22 08:14:48  haraldkipp
00048  * Made configurable
00049  *
00050  * Revision 1.2  2004/09/08 10:53:25  haraldkipp
00051  * os/timer.c
00052  *
00053  * Revision 1.1.1.1  2003/05/09 14:40:50  haraldkipp
00054  * Initial using 3.2.1
00055  *
00056  * Revision 1.4  2003/02/04 17:50:54  harald
00057  * Version 3 released
00058  *
00059  * Revision 1.3  2003/01/14 13:34:53  harald
00060  * *** empty log message ***
00061  *
00062  * Revision 1.2  2002/08/11 12:25:38  harald
00063  * ICC mods
00064  *
00065  * Revision 1.1  2002/08/02 14:25:09  harald
00066  * First check in
00067  *
00068  */
00069 
00070 /*
00071  * This header file specifies the hardware port bits. You
00072  * need to change or replace it, if your hardware differs.
00073  */
00074 #include <cfg/arch/avr.h>
00075 
00076 /*
00077  * The following header file contains the prototypes of
00078  * all global functions, which this module provides.
00079  */
00080 #include <dev/spidigio.h>
00081 
00082 /*
00083  * Determine ports, which had not been explicitly configured.
00084  */
00085 #ifndef SPIDIGIO_SOUT_BIT
00086 #define SPIDIGIO_SOUT_BIT 5
00087 #define SPIDIGIO_SOUT_AVRPORT AVRPORTD
00088 #define SPIDIGIO_SIN_BIT 6
00089 #define SPIDIGIO_SIN_AVRPORT AVRPORTD
00090 #define SPIDIGIO_SCLK_BIT 7
00091 #define SPIDIGIO_SCLK_AVRPORT AVRPORTD
00092 #define SPIDIGIO_LDI_BIT 7
00093 #define SPIDIGIO_LDI_AVRPORT AVRPORTB
00094 #define SPIDIGIO_LDO_BIT 5
00095 #define SPIDIGIO_LDO_AVRPORT AVRPORTB
00096 #endif
00097 
00098 #if (SPIDIGIO_SOUT_AVRPORT == AVRPORTB)
00099 #define SPIDIGIO_SOUT_PORT  PORTB
00100 #define SPIDIGIO_SOUT_DDR   DDRB
00101 
00102 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTD)
00103 #define SPIDIGIO_SOUT_PORT  PORTD
00104 #define SPIDIGIO_SOUT_DDR   DDRD
00105 
00106 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTE)
00107 #define SPIDIGIO_SOUT_PORT  PORTE
00108 #define SPIDIGIO_SOUT_DDR   DDRE
00109 
00110 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTF)
00111 #define SPIDIGIO_SOUT_PORT  PORTF
00112 #define SPIDIGIO_SOUT_DDR   DDRF
00113 
00114 #endif
00115 
00116 #if (SPIDIGIO_SIN_AVRPORT == AVRPORTB)
00117 #define SPIDIGIO_SIN_PORT   PORTB
00118 #define SPIDIGIO_SIN_PIN    PINB
00119 #define SPIDIGIO_SIN_DDR    DDRB
00120 
00121 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTD)
00122 #define SPIDIGIO_SIN_PORT   PORTD
00123 #define SPIDIGIO_SIN_PIN    PIND
00124 #define SPIDIGIO_SIN_DDR    DDRD
00125 
00126 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTE)
00127 #define SPIDIGIO_SIN_PORT   PORTE
00128 #define SPIDIGIO_SIN_PIN    PINE
00129 #define SPIDIGIO_SIN_DDR    DDRE
00130 
00131 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTF)
00132 #define SPIDIGIO_SIN_PORT   PORTF
00133 #define SPIDIGIO_SIN_PIN    PINF
00134 #define SPIDIGIO_SIN_DDR    DDRF
00135 #endif
00136 
00137 #if (SPIDIGIO_SCLK_AVRPORT == AVRPORTB)
00138 #define SPIDIGIO_SCLK_PORT  PORTB
00139 #define SPIDIGIO_SCLK_DDR   DDRB
00140 
00141 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTD)
00142 #define SPIDIGIO_SCLK_PORT  PORTD
00143 #define SPIDIGIO_SCLK_DDR   DDRD
00144 
00145 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTE)
00146 #define SPIDIGIO_SCLK_PORT  PORTE
00147 #define SPIDIGIO_SCLK_DDR   DDRE
00148 
00149 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTF)
00150 #define SPIDIGIO_SCLK_PORT  PORTF
00151 #define SPIDIGIO_SCLK_DDR   DDRF
00152 
00153 #endif /* SPIDIGIO_SCLK_AVRPORT */
00154 
00155 #if (SPIDIGIO_LDO_AVRPORT == AVRPORTB)
00156 #define SPIDIGIO_LDO_PORT   PORTB
00157 #define SPIDIGIO_LDO_DDR    DDRB
00158 
00159 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTD)
00160 #define SPIDIGIO_LDO_PORT   PORTD
00161 #define SPIDIGIO_LDO_DDR    DDRD
00162 
00163 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTE)
00164 #define SPIDIGIO_LDO_PORT   PORTE
00165 #define SPIDIGIO_LDO_DDR    DDRE
00166 
00167 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTF)
00168 #define SPIDIGIO_LDO_PORT   PORTF
00169 #define SPIDIGIO_LDO_DDR    DDRF
00170 
00171 #endif /* SPIDIGIO_LDO_AVRPORT */
00172 
00173 #if (SPIDIGIO_LDI_AVRPORT == AVRPORTB)
00174 #define SPIDIGIO_LDI_PORT   PORTB
00175 #define SPIDIGIO_LDI_DDR    DDRB
00176 
00177 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTD)
00178 #define SPIDIGIO_LDI_PORT   PORTD
00179 #define SPIDIGIO_LDI_DDR    DDRD
00180 
00181 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTE)
00182 #define SPIDIGIO_LDI_PORT   PORTE
00183 #define SPIDIGIO_LDI_DDR    DDRE
00184 
00185 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTF)
00186 #define SPIDIGIO_LDI_PORT   PORTF
00187 #define SPIDIGIO_LDI_DDR    DDRF
00188 
00189 #endif /* SPIDIGIO_LDI_AVRPORT */
00190 
00195 
00196 static ureg_t us_loops = 1;
00197 
00213 static INLINE void delay_us(ureg_t us)
00214 {
00215     ureg_t _cnt = us * us_loops;
00216 
00217     while (_cnt--) {
00218         /*
00219          * A no-operation assembly statement is used here.
00220          * Without this statement, the compiler may completely
00221          * wipe out the loop during optimization.
00222          */
00223         _NOP();
00224     }
00225 }
00226 
00238 static INLINE void ShiftDigital(void)
00239 {
00240     /* Switch clock line low. */
00241     cbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00242     /* Four microseconds delay. */
00243     delay_us(4);
00244 
00245     /* Switch clock line back high. */
00246     sbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00247     /* Four microseconds delay. */
00248     delay_us(4);
00249 }
00250 
00269 u_long SpiDigitalGet(ureg_t num)
00270 {
00271     u_long bits = 0;
00272 
00273     cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00274 
00275     /*
00276      * Toggle the input strobe. The shift register will latch
00277      * the current value at the parallel inputs into the shift
00278      * register.
00279      */
00280     sbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00281     delay_us(4);
00282     cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00283     delay_us(4);
00284 
00285     /* Loop for the specified number of bits. */
00286     while (num--) {
00287         /* Shift the resulting value first. */
00288         bits <<= 1;
00289 
00290         /*
00291          * The shift register's serial output pin is connected to 
00292          * the port input pin. If this input is high, set the least 
00293          * significant bit of the resulting value. Otherwise leave 
00294          * it zero.
00295          */
00296         if (bit_is_set(SPIDIGIO_SIN_PIN, SPIDIGIO_SIN_BIT)) {
00297             bits |= 1;
00298         }
00299 
00300         /* 
00301          * This will toggle the clock line, presenting the next bit
00302          * at the shift register's serial output pin.
00303          */
00304         ShiftDigital();
00305     }
00306     return bits;
00307 }
00308 
00323 void SpiDigitalSet(ureg_t num, u_long bits)
00324 {
00325     u_long mask;
00326 
00327     /* Nothing to do, if the number of bits is zero. */
00328     if (num) {
00329         /*
00330          * Create the bit mask of the most significant bit. Note the UL 
00331          * specifier. Most compilers will use integers by default, when 
00332          * calculating of the right side. They do not consider the left
00333          * side. In our case this would create unexpected results, if 
00334          * integers are 16 bit only.
00335          */
00336         mask = 1UL << (num - 1);
00337 
00338         /* Loop for the specified number of bits. */
00339         while (num--) {
00340 
00341             /*
00342              * The shift register input is connected to the CPU output.
00343              * If the currently masked bit is set, then set the CPU
00344              * output pin to high level. Otherwise set the output
00345              * pin to low.
00346              */
00347             if (bits & mask) {
00348                 /* Set bit instruction. */
00349                 sbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00350             } 
00351             else {
00352                 /* Clear bit instruction. */
00353                 cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00354             }
00355 
00356             /* Let the value get settled. */
00357             delay_us(4);
00358 
00359             /* Toggle the shift register clock line. */
00360             ShiftDigital();
00361 
00362             /* Left shift the mask by one. */
00363             mask >>= 1;
00364         }
00365 
00366         /*
00367          * Toggle the output strobe line. The shift register will latch
00368          * the shifted value and present it at its parallel output pins.
00369          */
00370         cbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00371         delay_us(4);
00372         sbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00373     }
00374 }
00375 
00401 static ureg_t CountDigitalShifts(ureg_t num, ureg_t bit, ureg_t smode)
00402 {
00403     ureg_t i;
00404     ureg_t rc = 0;
00405 
00406     /*
00407      * Toggle input strobe if we are searching the last modified bit.
00408      * Input lines are latched on the falling edge.
00409      */
00410     if (smode) {
00411         sbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00412         delay_us(4);
00413         cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00414     }
00415 
00416     /*
00417      * Set the shift register input.
00418      */
00419     if (bit) {
00420         sbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00421     } else {
00422         cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00423     }
00424     delay_us(4);
00425 
00426     /*
00427      * Do the requested number of shifts and watch the requested bit 
00428      * position.
00429      */
00430     for (i = 0; i < num; i++) {
00431         if (bit_is_set(SPIDIGIO_SIN_PIN, SPIDIGIO_SIN_BIT)) {
00432             if (bit) {
00433                 if (smode) {
00434                     rc = i + 1;
00435                 } else if (rc == 0) {
00436                     rc = i + 1;
00437                 }
00438             }
00439         } else {
00440             if (bit == 0) {
00441                 if (smode) {
00442                     rc = i + 1;
00443                 } else if (rc == 0) {
00444                     rc = i + 1;
00445                 }
00446             }
00447         }
00448         ShiftDigital();
00449     }
00450     return rc;
00451 }
00452 
00468 void SpiDigitalInit(ureg_t * inputs, ureg_t * outputs)
00469 {
00470     ureg_t total = 0;
00471     ureg_t i;
00472     ureg_t cnt;
00473 
00474     /*
00475      * Determine the delay loop count based on the CPU clock.
00476      */
00477     if ((us_loops = (NutGetCpuClock() + 500000UL) / 4000000UL) < 1) {
00478         us_loops = 1;
00479     }
00480 
00481     /*
00482      * Set serial data output line direction.
00483      */
00484     sbi(SPIDIGIO_SOUT_DDR, SPIDIGIO_SOUT_BIT);
00485 
00486     /*
00487      * Set serial data input line direction and enable pullup.
00488      */
00489     sbi(SPIDIGIO_SIN_PORT, SPIDIGIO_SIN_BIT);
00490     cbi(SPIDIGIO_SIN_DDR, SPIDIGIO_SIN_BIT);
00491 
00492     /*
00493      * Clock. Input data is shifted on the falling, output data on the 
00494      * rising edge.
00495      */
00496     sbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00497     sbi(SPIDIGIO_SCLK_DDR, SPIDIGIO_SCLK_BIT);
00498 
00499     /*
00500      * UCN5841 output strobe. Shift register data appears on the output 
00501      * pins as long as this line is held high.
00502      */
00503     sbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00504     sbi(SPIDIGIO_LDO_DDR, SPIDIGIO_LDO_BIT);
00505 
00506     /*
00507      * SN74HC165 input strobe. Data at input pins is latched in the shift 
00508      * register on the falling edge.
00509      */
00510     cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00511     sbi(SPIDIGIO_LDI_DDR, SPIDIGIO_LDI_BIT);
00512 
00513     /*
00514      * Fill the shift register with zeros. Then shift in ones until the 
00515      * first appears on the output. This gives us the total size plus one 
00516      * of the shift register.
00517      */
00518     CountDigitalShifts(32, 0, 0);
00519     total = CountDigitalShifts(32, 1, 0) - 1;
00520 
00521     /*
00522      * Determine the number of inputs. We do this five times for zeros 
00523      * and ones and take the maximum count. This way we compensate 
00524      * changing inputs while counting.
00525      */
00526     *inputs = 0;
00527     for (i = 0; i < 5; i++) {
00528         if ((cnt = CountDigitalShifts(total, 0, 1)) > *inputs) {
00529             *inputs = cnt;
00530         }
00531         if ((cnt = CountDigitalShifts(total, 1, 1)) > *inputs) {
00532             *inputs = cnt;
00533         }
00534     }
00535     *outputs = total - *inputs;
00536 }
00537 
00538 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/