1  | //------------------------------------------------------------------------------------------------------------
  | 
2  | //
  | 
3  | //       Programmname: STM32F4_AD7680
  | 
4  | //              Datum: 09.10.2013
  | 
5  | //                IDE: CooCox COIDE V. 1.7.5
  | 
6  | //              Autor: T. Öztürk
  | 
7  | //
  | 
8  | //
  | 
9  | //
  | 
10  | //    Takt-Einstellungen: HSE=8 MHz, HCLK=168MHz, SYSCLK=168 MHz, PCLK1=42 MHz (Taktquelle für SPI2)
  | 
11  | //              PLL_N=336
  | 
12  | //              PLL_M=25
  | 
13  | //              PLL_Q=7
  | 
14  | //              PLL_P=2
  | 
15  | //
  | 
16  | //    Beschreibung: Dieses Programm steuert über Serial Peripheral Interface (SPI) den externen
  | 
17  | //                  Analog-Digital Umsetzer AD7680 an und empfängt die Abtastwerte.        Der AD7680
  | 
18  | //                  hat eine Auflösung von 16 Bit, also 65535 Abstufungen. Der 16-Bit Abtastwert
  | 
19  | //                  wird nicht "direkt" als 16-Bit geschickt, sondern in 24 Bits, d.h. der AD7680
  | 
20  | //                  füllt die restlichen Bit-Stellen mit Nullen auf. Der SPI wurde daher auf 8 Bit,
  | 
21  | //                  also 1 Byte, Datenpakete eingestellt. Es werden somit drei Byte Pakete empfangen,
  | 
22  | //                  aus denen der 16 Bit Abtastwert durch Bit-Verschiebung zusammengestellt wird.
  | 
23  | //                  Der Abtastvorgang beginnt mit der fallenden Flanke des Chip Select Signals.
  | 
24  | //                  Die Taktgeschwindigkeit SCLK des SPI's bestimmt auch die Abtastrate des  AD7680.
  | 
25  | //                  Weitere Informationen sind dem Datenblatt des AD7680 zu entnehmen:
  | 
26  | //                  http://www.analog.com/static/imported-files/data_sheets/AD7680.pdf
  | 
27  | //                  (In CooCox CoIDE: STRG gedrückt halten und mit linken Mauszeiger auf Link klicken)
  | 
28  | 
  | 
29  | //                  In diesem Beispiel wird der SPI2 Controller des STM32F4 verwendet.  Die jeweiligen
  | 
30  | //                  Pin-Zuordnungen sind dem Datenblatt des STM32F4 zu entnehmen. (Abschnitt 3, Tab. 6):
  | 
31  | //                  http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/DM00037051.pdf
  | 
32  | //
  | 
33  | //                  Eine weitere wichtige Quelle bzw. die einzige, die ich gefunden habe, wo der AD7680 eingesetzt wird:
  | 
34  | //                  http://abieneman.wordpress.com/2010/04/04/punch-acceleration-sensor/
  | 
35  | //
  | 
36  | //
  | 
37  | //          Anschlussplan:
  | 
38  | //
  | 
39  | //          AD7860             |    STM32F4 Discovery Board
  | 
40  | //------------------------------------------------------------------------------------------------------------
  | 
41  | //          VDD   (Pin 1)        VDD
  | 
42  | //          GND   (Pin2,Pin3)    GND
  | 
43  | //          VIN   (Pin4)         zu Messende Spannung z.B. VDD, Auf max. Spannung achten !
  | 
44  | //          SCLK  (Pin5)         PB10
  | 
45  | //          NC    (Pin6)          -
  | 
46  | //          SDATA (Pin7)         PC2
  | 
47  | //          ~CS   (Pin8)         PA3
  | 
48  | //------------------------------------------------------------------------------------------------------------
  | 
49  | //
  | 
50  | //          Ich habe keine Kondensatoren zum Glätten bzw. Entstören verwendet. Dies ist lediglich ein
  | 
51  | //          Minimalbeispiel. Eine Beispiel-Schaltung nach Analog Devices befindet sich im Datenblatt
  | 
52  | //          des AD7680 auf S. 13 (inklusive "saubere" Versorgungs- bzw. Referenzspannung)
  | 
53  | //
  | 
54  | //          Ich bin kein "langjähriger" C-Programmierer, daher entschuldigt den nicht sauberen
  | 
55  | //          bzw. nicht professionellen Code !
  | 
56  | //
  | 
57  | //
  | 
58  | //          Viel Spass und Erfolg !
  | 
59  | //
  | 
60  | //------------------------------------------------------------------------------------------------------------
  | 
61  | 
  | 
62  | 
  | 
63  | 
  | 
64  | //---------------------------------------------------------------------------------------------------
  | 
65  | // Includes
  | 
66  | //---------------------------------------------------------------------------------------------------
  | 
67  | #include "stm32f4xx_rcc.h"
  | 
68  | #include "stm32f4xx_gpio.h"
  | 
69  | #include "stm32f4xx_spi.h"
  | 
70  | 
  | 
71  | 
  | 
72  | 
  | 
73  | //---------------------------------------------------------------------------------------------------
  | 
74  | // Defines
  | 
75  | //---------------------------------------------------------------------------------------------------
  | 
76  | #define LED_Green     GPIO_Pin_12   // selbsterklärend
  | 
77  | #define LED_Orange    GPIO_Pin_13   //     ""
  | 
78  | #define LED_Red       GPIO_Pin_14   //    ""
  | 
79  | #define LED_Blue      GPIO_Pin_15   //     ""
  | 
80  | 
  | 
81  | #define SCLK          GPIO_Pin_10   //PB10  // Takt-Ausgang für SPI
  | 
82  | #define SDATA         GPIO_Pin_2    //PC2   // Datenleitung für die Samples bzw. MISO
  | 
83  | #define MOSI          GPIO_Pin_3    //PC3   // MOSI; existiert in diesem Beispiel nicht
  | 
84  | #define CS            GPIO_Pin_3    //PA3   // ChipSelect (auch SlaveSelect genannt)
  | 
85  | 
  | 
86  | #define ADC_RES       65535    // Der AD7680 hat eine Auflösung von 16 Bit: (2^16)-1=65535 Abstufungen
  | 
87  | #define Vref          2.94     // Referenzsspannung des A-D Umsetzer ist bei beim AD7680 die Versorgungsspannung Vss
  | 
88  |                                // Die Versorgungsspannung des AD-Umsetzer wird dem Vdd Pin des STM32F4 Discovery Boards
  | 
89  |                                // entnommen, die bei USB Speisung c.a. 2.94 V beträgt (Schutzdioden auf dem Board...).
  | 
90  | 
  | 
91  | //---------------------------------------------------------------------------------------------------
  | 
92  | // Prototpes
  | 
93  | //---------------------------------------------------------------------------------------------------
  | 
94  | void EnableRCC(void);
  | 
95  | void ConfigGPIO(void);
  | 
96  | void ConfigSPI(void);
  | 
97  | uint8_t ReadSPI(void);
  | 
98  | 
  | 
99  | 
  | 
100  | int main(void)
  | 
101  | {
 | 
102  | 
  | 
103  |   uint8_t  raw_bytes[3];        // Ein Array wo die drei "Roh-Bytes" gespeichert werden. "Roh-Bytes" sind die mit den Nullen aufgefüllten Bytes, die  vom AD7680 gesendet werden
  | 
104  |   unsigned sample_value = 0;    // Hier wird der 16 Bit Abtastwert gespeichert
  | 
105  |   float voltage_value = 0;      // Der berechnete Spannungswert wird hier gespeichert
  | 
106  |   int i = 0;
  | 
107  | 
  | 
108  |   SystemInit();            // ggf. nötig, damit die Takteinstellungen richtig übernommen werden
  | 
109  |   EnableRCC();             // Takt für die benötigten Peripherien einschalten
  | 
110  |   ConfigGPIO();            // Die GPIO Pins konfigurieren
  | 
111  |   ConfigSPI();             // SPI2 Controller konfigurieren
  | 
112  |   GPIO_SetBits(GPIOD,LED_Blue);  // blaue LED einschalten (Lebenszeichen)
  | 
113  | 
  | 
114  |     while(1)
  | 
115  |     {
 | 
116  |       GPIO_ResetBits(GPIOA,CS);  // ChipSelect Ausgang auf Low setzen, damit der AD7680 mit Abtastvorgang anfängt
  | 
117  |       for(i=0; i<3; i++)
  | 
118  |       {
 | 
119  |         raw_bytes[i]=ReadSPI();  // Alle drei "Roh-Bytes" in das Array speichern
  | 
120  |       }
  | 
121  |       GPIO_SetBits(GPIOA,CS);    // ChipSelect Ausgang auf High setzen, damit der AD7680 mit Abtastvorgang aufhört
  | 
122  | 
  | 
123  | 
  | 
124  |       sample_value = (raw_bytes[0] << 11) | (raw_bytes[1] << 3) | (raw_bytes[2] >> 5);   // Da wir 3 Bytes (24 Bit) erhalten, müssen wir Bits verschieben um an die
  | 
125  |                                                 // relevanten 16 Bit Daten (Abtastwert) heranzukommen.
  | 
126  | 
  | 
127  |       voltage_value=(Vref/ADC_RES)*sample_value;  // Spannungswert aus dem Abtastwert (16 Bit ADC Wert) berechnen
  | 
128  |     }
  | 
129  | }
  | 
130  | 
  | 
131  | 
  | 
132  | uint8_t ReadSPI(void)
  | 
133  | {
 | 
134  |     SPI2->DR = 0xAA;                           // "dummy" Daten schreiben, dies ist nötig, damit überhaupt Empfangen werden kann.
  | 
135  |     while( !(SPI2->SR & SPI_I2S_FLAG_TXE) );   // warten bis der Sendevorgang abgeschlossen ist
  | 
136  |     while( !(SPI2->SR & SPI_I2S_FLAG_RXNE) );  // warten bis der Datenempfang abgeschlossen ist
  | 
137  |     while( SPI2->SR & SPI_I2S_FLAG_BSY );      // warten bis der SPI2 nicht mehr "beschäftigt" ist.
  | 
138  |     return SPI2->DR;                           // Daten im SPI2 Datenregister als Rückgabewert ausgeben
  | 
139  | }
  | 
140  | 
  | 
141  | 
  | 
142  | void ConfigSPI()
  | 
143  | {
 | 
144  | 
  | 
145  |   SPI_InitTypeDef SPI_InitStructure;
  | 
146  | 
  | 
147  |   SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;      // Daten empfangen und senden
  | 
148  |   SPI_InitStructure.SPI_Mode    = SPI_Mode_Master;               // STM32F407VGT ist der Master und AD7680 der Slace
  | 
149  |   SPI_InitStructure.SPI_DataSize   = SPI_DataSize_8b;           // Es werden 24 Bit's vom AD7680 ausgegeben 000016_Bit_Sample_Value0000, daher 3 x 8 Bit Packete
  | 
150  |   SPI_InitStructure.SPI_DataSize   = SPI_FirstBit_MSB;          // Der AD7680 überträgt die Daten als "MSB first"
  | 
151  |   SPI_InitStructure.SPI_CPOL     = SPI_CPOL_High;               // Clock Polarity (CPOL) = 1
  | 
152  |   SPI_InitStructure.SPI_CPHA     = SPI_CPHA_2Edge;              // Clock Phase (CPHA) = 1 (entspricht hier CPHA_2Edge !)
  | 
153  |   SPI_InitStructure.SPI_NSS     = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;  // NSS Pin als GPIO Pin einstellen und intern auf High setzen
  | 
154  | 
  | 
155  |   SPI_InitStructure.SPI_BaudRatePrescaler  =SPI_BaudRatePrescaler_64;      // SCLK=42MHz/64=656.2kHz APB1=42 MHz, SYSCLK=168 MHz
  | 
156  |   SPI_InitStructure.SPI_CRCPolynomial   = 0;                   // kein CRC bilden
  | 
157  | 
  | 
158  |   SPI_Init(SPI2, &SPI_InitStructure);                          // SPI2 Interface initialisieren
  | 
159  |   SPI_CalculateCRC(SPI2, DISABLE);                             // CRC Berechnung deaktivieren
  | 
160  | 
  | 
161  |   SPI_Cmd(SPI2,ENABLE);                                        // SPI2 Interface aktivieren
  | 
162  | }
  | 
163  | 
  | 
164  | 
  | 
165  | void EnableRCC(void)
  | 
166  | {
 | 
167  |   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);  // Takt für die GPIOA's aktivieren
  | 
168  |   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  // Takt für die GPIOB's aktivieren
  | 
169  |   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);  // Takt für die GPIOC's aktivieren
  | 
170  |   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);  // Takt für die GPIOD's aktivieren
  | 
171  |   RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,  ENABLE);  // Takt für den SPI2 aktivieren
  | 
172  | }
  | 
173  | 
  | 
174  | 
  | 
175  | void ConfigGPIO(void)
  | 
176  | {
 | 
177  | 
  | 
178  |   GPIO_InitTypeDef GPIO_InitStructure;
  | 
179  | 
  | 
180  | 
  | 
181  |   //---------------------------------------------------------------------------------------------------
  | 
182  |   // Pin für die blaue LED (PD15) konfigurieren
  | 
183  |   //---------------------------------------------------------------------------------------------------
  | 
184  |   GPIO_InitStructure.GPIO_Pin   = LED_Blue;
  | 
185  |   GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_OUT;
  | 
186  |   GPIO_InitStructure.GPIO_OType  = GPIO_OType_PP;
  | 
187  |   GPIO_InitStructure.GPIO_PuPd   = GPIO_PuPd_NOPULL;
  | 
188  |   GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
  | 
189  | 
  | 
190  |   GPIO_Init(GPIOD, &GPIO_InitStructure);
  | 
191  |   //---------------------------------------------------------------------------------------------------
  | 
192  | 
  | 
193  | 
  | 
194  | 
  | 
195  |   //---------------------------------------------------------------------------------------------------
  | 
196  |   // SCLK Ausgang konfigurieren
  | 
197  |   // -restliche Einstellungen können aus der LED Konfiguration übernommen werden.
  | 
198  |   //---------------------------------------------------------------------------------------------------
  | 
199  |   GPIO_InitStructure.GPIO_Pin   = SCLK;
  | 
200  |   GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;       // SCLK, also PB10, auf Alternativ-Modus umschalten
  | 
201  | 
  | 
202  |   GPIO_Init(GPIOB, &GPIO_InitStructure);
  | 
203  | 
  | 
204  |   GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);   // PB10 mit dem SPI2 Controller verbinden
  | 
205  |                                  // PB10 ist nun die SCLK Quelle
  | 
206  |   //---------------------------------------------------------------------------------------------------
  | 
207  | 
  | 
208  | 
  | 
209  | 
  | 
210  |   //---------------------------------------------------------------------------------------------------
  | 
211  |   // SDATA (MISO) und MOSI konfigurieren
  | 
212  |   //---------------------------------------------------------------------------------------------------
  | 
213  |   GPIO_InitStructure.GPIO_Pin   = SDATA | MOSI;
  | 
214  |   GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;      // PC2 und PC3 auf Alternativ-Modus umschalten
  | 
215  | 
  | 
216  |   GPIO_Init(GPIOC, &GPIO_InitStructure);
  | 
217  | 
  | 
218  |   GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_SPI2); // PC2 mit SDATA(MISO) von SPI2 verbinden
  | 
219  |   GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); // PC3 mit MOSI von SPI2 verbinden
  | 
220  |   //---------------------------------------------------------------------------------------------------
  | 
221  | 
  | 
222  | 
  | 
223  | 
  | 
224  |   //---------------------------------------------------------------------------------------------------
  | 
225  |   // ChipSelect(CS) bzw. SlaveSelect(SS) konfigurieren
  | 
226  |   //---------------------------------------------------------------------------------------------------
  | 
227  |   GPIO_InitStructure.GPIO_Pin   = CS;
  | 
228  |   GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_OUT;    // Normaler Ausgang...
  | 
229  |   GPIO_InitStructure.GPIO_PuPd   = GPIO_PuPd_UP;     // PullUp aktivieren
  | 
230  |   GPIO_Init(GPIOA, &GPIO_InitStructure);
  | 
231  | 
  | 
232  |   GPIO_SetBits(GPIOA,CS);                            // ChipSelect sofort auf High setzen,
  | 
233  |                                                      // damit der AD7680 nicht gleich anfängt
  | 
234  |                                                      // zu "arbeiten".
  | 
235  |   //---------------------------------------------------------------------------------------------------
  | 
236  | }
  |