Nut/OS  4.10.3
API Reference
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$
00036  * Revision 1.2  2008/08/11 06:59:17  haraldkipp
00037  * BSD types replaced by stdint types (feature request #1282721).
00038  *
00039  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00040  * Moved from dev.
00041  *
00042  * Revision 1.5  2005/02/02 19:59:12  haraldkipp
00043  * Typo corrected. Compiler failed, if ports were not configured. Due
00044  * to the completely broken port configuration this error wasn't
00045  * detected.
00046  *
00047  * Revision 1.4  2005/01/22 19:25:14  haraldkipp
00048  * Changed AVR port configuration names from PORTx to AVRPORTx.
00049  *
00050  * Revision 1.3  2004/09/22 08:14:48  haraldkipp
00051  * Made configurable
00052  *
00053  * Revision 1.2  2004/09/08 10:53:25  haraldkipp
00054  * os/timer.c
00055  *
00056  * Revision 1.1.1.1  2003/05/09 14:40:50  haraldkipp
00057  * Initial using 3.2.1
00058  *
00059  * Revision 1.4  2003/02/04 17:50:54  harald
00060  * Version 3 released
00061  *
00062  * Revision 1.3  2003/01/14 13:34:53  harald
00063  * *** empty log message ***
00064  *
00065  * Revision 1.2  2002/08/11 12:25:38  harald
00066  * ICC mods
00067  *
00068  * Revision 1.1  2002/08/02 14:25:09  harald
00069  * First check in
00070  *
00071  */
00072 
00073 /*
00074  * This header file specifies the hardware port bits. You
00075  * need to change or replace it, if your hardware differs.
00076  */
00077 #include <cfg/arch/avr.h>
00078 
00079 /*
00080  * The following header file contains the prototypes of
00081  * all global functions, which this module provides.
00082  */
00083 #include <dev/spidigio.h>
00084 
00085 /*
00086  * Determine ports, which had not been explicitly configured.
00087  */
00088 #ifndef SPIDIGIO_SOUT_BIT
00089 #define SPIDIGIO_SOUT_BIT 5
00090 #define SPIDIGIO_SOUT_AVRPORT AVRPORTD
00091 #define SPIDIGIO_SIN_BIT 6
00092 #define SPIDIGIO_SIN_AVRPORT AVRPORTD
00093 #define SPIDIGIO_SCLK_BIT 7
00094 #define SPIDIGIO_SCLK_AVRPORT AVRPORTD
00095 #define SPIDIGIO_LDI_BIT 7
00096 #define SPIDIGIO_LDI_AVRPORT AVRPORTB
00097 #define SPIDIGIO_LDO_BIT 5
00098 #define SPIDIGIO_LDO_AVRPORT AVRPORTB
00099 #endif
00100 
00101 #if (SPIDIGIO_SOUT_AVRPORT == AVRPORTB)
00102 #define SPIDIGIO_SOUT_PORT  PORTB
00103 #define SPIDIGIO_SOUT_DDR   DDRB
00104 
00105 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTD)
00106 #define SPIDIGIO_SOUT_PORT  PORTD
00107 #define SPIDIGIO_SOUT_DDR   DDRD
00108 
00109 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTE)
00110 #define SPIDIGIO_SOUT_PORT  PORTE
00111 #define SPIDIGIO_SOUT_DDR   DDRE
00112 
00113 #elif (SPIDIGIO_SOUT_AVRPORT == AVRPORTF)
00114 #define SPIDIGIO_SOUT_PORT  PORTF
00115 #define SPIDIGIO_SOUT_DDR   DDRF
00116 
00117 #endif
00118 
00119 #if (SPIDIGIO_SIN_AVRPORT == AVRPORTB)
00120 #define SPIDIGIO_SIN_PORT   PORTB
00121 #define SPIDIGIO_SIN_PIN    PINB
00122 #define SPIDIGIO_SIN_DDR    DDRB
00123 
00124 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTD)
00125 #define SPIDIGIO_SIN_PORT   PORTD
00126 #define SPIDIGIO_SIN_PIN    PIND
00127 #define SPIDIGIO_SIN_DDR    DDRD
00128 
00129 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTE)
00130 #define SPIDIGIO_SIN_PORT   PORTE
00131 #define SPIDIGIO_SIN_PIN    PINE
00132 #define SPIDIGIO_SIN_DDR    DDRE
00133 
00134 #elif (SPIDIGIO_SIN_AVRPORT == AVRPORTF)
00135 #define SPIDIGIO_SIN_PORT   PORTF
00136 #define SPIDIGIO_SIN_PIN    PINF
00137 #define SPIDIGIO_SIN_DDR    DDRF
00138 #endif
00139 
00140 #if (SPIDIGIO_SCLK_AVRPORT == AVRPORTB)
00141 #define SPIDIGIO_SCLK_PORT  PORTB
00142 #define SPIDIGIO_SCLK_DDR   DDRB
00143 
00144 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTD)
00145 #define SPIDIGIO_SCLK_PORT  PORTD
00146 #define SPIDIGIO_SCLK_DDR   DDRD
00147 
00148 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTE)
00149 #define SPIDIGIO_SCLK_PORT  PORTE
00150 #define SPIDIGIO_SCLK_DDR   DDRE
00151 
00152 #elif (SPIDIGIO_SCLK_AVRPORT == AVRPORTF)
00153 #define SPIDIGIO_SCLK_PORT  PORTF
00154 #define SPIDIGIO_SCLK_DDR   DDRF
00155 
00156 #endif /* SPIDIGIO_SCLK_AVRPORT */
00157 
00158 #if (SPIDIGIO_LDO_AVRPORT == AVRPORTB)
00159 #define SPIDIGIO_LDO_PORT   PORTB
00160 #define SPIDIGIO_LDO_DDR    DDRB
00161 
00162 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTD)
00163 #define SPIDIGIO_LDO_PORT   PORTD
00164 #define SPIDIGIO_LDO_DDR    DDRD
00165 
00166 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTE)
00167 #define SPIDIGIO_LDO_PORT   PORTE
00168 #define SPIDIGIO_LDO_DDR    DDRE
00169 
00170 #elif (SPIDIGIO_LDO_AVRPORT == AVRPORTF)
00171 #define SPIDIGIO_LDO_PORT   PORTF
00172 #define SPIDIGIO_LDO_DDR    DDRF
00173 
00174 #endif /* SPIDIGIO_LDO_AVRPORT */
00175 
00176 #if (SPIDIGIO_LDI_AVRPORT == AVRPORTB)
00177 #define SPIDIGIO_LDI_PORT   PORTB
00178 #define SPIDIGIO_LDI_DDR    DDRB
00179 
00180 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTD)
00181 #define SPIDIGIO_LDI_PORT   PORTD
00182 #define SPIDIGIO_LDI_DDR    DDRD
00183 
00184 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTE)
00185 #define SPIDIGIO_LDI_PORT   PORTE
00186 #define SPIDIGIO_LDI_DDR    DDRE
00187 
00188 #elif (SPIDIGIO_LDI_AVRPORT == AVRPORTF)
00189 #define SPIDIGIO_LDI_PORT   PORTF
00190 #define SPIDIGIO_LDI_DDR    DDRF
00191 
00192 #endif /* SPIDIGIO_LDI_AVRPORT */
00193 
00198 
00199 static ureg_t us_loops = 1;
00200 
00216 static INLINE void delay_us(ureg_t us)
00217 {
00218     ureg_t _cnt = us * us_loops;
00219 
00220     while (_cnt--) {
00221         /*
00222          * A no-operation assembly statement is used here.
00223          * Without this statement, the compiler may completely
00224          * wipe out the loop during optimization.
00225          */
00226         _NOP();
00227     }
00228 }
00229 
00241 static INLINE void ShiftDigital(void)
00242 {
00243     /* Switch clock line low. */
00244     cbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00245     /* Four microseconds delay. */
00246     delay_us(4);
00247 
00248     /* Switch clock line back high. */
00249     sbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00250     /* Four microseconds delay. */
00251     delay_us(4);
00252 }
00253 
00272 uint32_t SpiDigitalGet(ureg_t num)
00273 {
00274     uint32_t bits = 0;
00275 
00276     cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00277 
00278     /*
00279      * Toggle the input strobe. The shift register will latch
00280      * the current value at the parallel inputs into the shift
00281      * register.
00282      */
00283     sbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00284     delay_us(4);
00285     cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00286     delay_us(4);
00287 
00288     /* Loop for the specified number of bits. */
00289     while (num--) {
00290         /* Shift the resulting value first. */
00291         bits <<= 1;
00292 
00293         /*
00294          * The shift register's serial output pin is connected to 
00295          * the port input pin. If this input is high, set the least 
00296          * significant bit of the resulting value. Otherwise leave 
00297          * it zero.
00298          */
00299         if (bit_is_set(SPIDIGIO_SIN_PIN, SPIDIGIO_SIN_BIT)) {
00300             bits |= 1;
00301         }
00302 
00303         /* 
00304          * This will toggle the clock line, presenting the next bit
00305          * at the shift register's serial output pin.
00306          */
00307         ShiftDigital();
00308     }
00309     return bits;
00310 }
00311 
00326 void SpiDigitalSet(ureg_t num, uint32_t bits)
00327 {
00328     uint32_t mask;
00329 
00330     /* Nothing to do, if the number of bits is zero. */
00331     if (num) {
00332         /*
00333          * Create the bit mask of the most significant bit. Note the UL 
00334          * specifier. Most compilers will use integers by default, when 
00335          * calculating of the right side. They do not consider the left
00336          * side. In our case this would create unexpected results, if 
00337          * integers are 16 bit only.
00338          */
00339         mask = 1UL << (num - 1);
00340 
00341         /* Loop for the specified number of bits. */
00342         while (num--) {
00343 
00344             /*
00345              * The shift register input is connected to the CPU output.
00346              * If the currently masked bit is set, then set the CPU
00347              * output pin to high level. Otherwise set the output
00348              * pin to low.
00349              */
00350             if (bits & mask) {
00351                 /* Set bit instruction. */
00352                 sbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00353             } 
00354             else {
00355                 /* Clear bit instruction. */
00356                 cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00357             }
00358 
00359             /* Let the value get settled. */
00360             delay_us(4);
00361 
00362             /* Toggle the shift register clock line. */
00363             ShiftDigital();
00364 
00365             /* Left shift the mask by one. */
00366             mask >>= 1;
00367         }
00368 
00369         /*
00370          * Toggle the output strobe line. The shift register will latch
00371          * the shifted value and present it at its parallel output pins.
00372          */
00373         cbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00374         delay_us(4);
00375         sbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00376     }
00377 }
00378 
00404 static ureg_t CountDigitalShifts(ureg_t num, ureg_t bit, ureg_t smode)
00405 {
00406     ureg_t i;
00407     ureg_t rc = 0;
00408 
00409     /*
00410      * Toggle input strobe if we are searching the last modified bit.
00411      * Input lines are latched on the falling edge.
00412      */
00413     if (smode) {
00414         sbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00415         delay_us(4);
00416         cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00417     }
00418 
00419     /*
00420      * Set the shift register input.
00421      */
00422     if (bit) {
00423         sbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00424     } else {
00425         cbi(SPIDIGIO_SOUT_PORT, SPIDIGIO_SOUT_BIT);
00426     }
00427     delay_us(4);
00428 
00429     /*
00430      * Do the requested number of shifts and watch the requested bit 
00431      * position.
00432      */
00433     for (i = 0; i < num; i++) {
00434         if (bit_is_set(SPIDIGIO_SIN_PIN, SPIDIGIO_SIN_BIT)) {
00435             if (bit) {
00436                 if (smode) {
00437                     rc = i + 1;
00438                 } else if (rc == 0) {
00439                     rc = i + 1;
00440                 }
00441             }
00442         } else {
00443             if (bit == 0) {
00444                 if (smode) {
00445                     rc = i + 1;
00446                 } else if (rc == 0) {
00447                     rc = i + 1;
00448                 }
00449             }
00450         }
00451         ShiftDigital();
00452     }
00453     return rc;
00454 }
00455 
00471 void SpiDigitalInit(ureg_t * inputs, ureg_t * outputs)
00472 {
00473     ureg_t total = 0;
00474     ureg_t i;
00475     ureg_t cnt;
00476 
00477     /*
00478      * Determine the delay loop count based on the CPU clock.
00479      */
00480     if ((us_loops = (NutGetCpuClock() + 500000UL) / 4000000UL) < 1) {
00481         us_loops = 1;
00482     }
00483 
00484     /*
00485      * Set serial data output line direction.
00486      */
00487     sbi(SPIDIGIO_SOUT_DDR, SPIDIGIO_SOUT_BIT);
00488 
00489     /*
00490      * Set serial data input line direction and enable pullup.
00491      */
00492     sbi(SPIDIGIO_SIN_PORT, SPIDIGIO_SIN_BIT);
00493     cbi(SPIDIGIO_SIN_DDR, SPIDIGIO_SIN_BIT);
00494 
00495     /*
00496      * Clock. Input data is shifted on the falling, output data on the 
00497      * rising edge.
00498      */
00499     sbi(SPIDIGIO_SCLK_PORT, SPIDIGIO_SCLK_BIT);
00500     sbi(SPIDIGIO_SCLK_DDR, SPIDIGIO_SCLK_BIT);
00501 
00502     /*
00503      * UCN5841 output strobe. Shift register data appears on the output 
00504      * pins as long as this line is held high.
00505      */
00506     sbi(SPIDIGIO_LDO_PORT, SPIDIGIO_LDO_BIT);
00507     sbi(SPIDIGIO_LDO_DDR, SPIDIGIO_LDO_BIT);
00508 
00509     /*
00510      * SN74HC165 input strobe. Data at input pins is latched in the shift 
00511      * register on the falling edge.
00512      */
00513     cbi(SPIDIGIO_LDI_PORT, SPIDIGIO_LDI_BIT);
00514     sbi(SPIDIGIO_LDI_DDR, SPIDIGIO_LDI_BIT);
00515 
00516     /*
00517      * Fill the shift register with zeros. Then shift in ones until the 
00518      * first appears on the output. This gives us the total size plus one 
00519      * of the shift register.
00520      */
00521     CountDigitalShifts(32, 0, 0);
00522     total = CountDigitalShifts(32, 1, 0) - 1;
00523 
00524     /*
00525      * Determine the number of inputs. We do this five times for zeros 
00526      * and ones and take the maximum count. This way we compensate 
00527      * changing inputs while counting.
00528      */
00529     *inputs = 0;
00530     for (i = 0; i < 5; i++) {
00531         if ((cnt = CountDigitalShifts(total, 0, 1)) > *inputs) {
00532             *inputs = cnt;
00533         }
00534         if ((cnt = CountDigitalShifts(total, 1, 1)) > *inputs) {
00535             *inputs = cnt;
00536         }
00537     }
00538     *outputs = total - *inputs;
00539 }
00540 
00541