Hi, Ich möchte über SPI einen AS5048A auslesen (http://ams.com/eng/Products/Magnetic-Position-Sensors/Angle-Position-On-Axis/AS5048A). Ich habe es mit und ohne DMA versucht und unterschiedliche Ergebnisse erhalten: Ohne DMA scheint es mir den Korrekten Wert anzugeben, jedoch ist der Wert nur zwischen 8192 und 16384, also nur die halbe Auflösung. Dabei habe ich über die SPI Schnittstelle einfach ein 0x3FFF geschickt, was mir nach Datenblatt die 14Bit Auflösung zurückschicken sollte. Damit könnte ich aber noch leben. Sobald ich aber den DMA verwende, passiert etwas merkwürdiges. Die Auflösung scheint zwischen 2 Werten hin und her zu springen (zufällig, manchmal auch 3mal einen Wert aus dem ersten Intervall etc.), ein Wert liegt im Intervall [0,8191], der andere im Intervall [8192,16384]. Was ich mache: Isch schreibe in meinen TX Buffer einfach ein 0x3FFF rein und lesen nach Bestätigung durch einen Interrupt den RX Buffer aus. Das Problem ist, dass ich nicht einfach +8192 für Werte aus dem ersten Intervall nehmen kann, da die Differenz zum zweiten Intervall nicht linear ist. Im Datenblatt auf Seite 2 und 3 wird ja ein Vorgehen beschrieben, doch ich möchte eigentlich nur den Winkel und dafür sollte 0x3FFF reichen. Jemand eine Idee? Edit: Ich habe gerade gesehen, dass die Werte nicht nur zwischen den Intervallen springen, sondern auch innerhalb, z.B zwischen 10000 und 15000. Grüsse Sebastian T.
:
Bearbeitet durch User
Sebastian T. schrieb: > Dabei > habe ich über die SPI Schnittstelle einfach ein 0x3FFF geschickt, was > mir nach Datenblatt die 14Bit Auflösung zurückschicken sollte. Nach dem hier: >> For a single READ command two transmission sequences are necessary. >> The first package written to the AS5048 contains the READ command >> (MSB-1 high) and the address the chip has to access, entspricht ein 3fff dem Schreibkommando auf das Winkel-Register, statt zu lesen und der Rückgabewert ist demgemäß irgendwas. > Im Datenblatt auf Seite 2 und 3 wird ja ein Vorgehen beschrieben, doch > ich möchte eigentlich nur den Winkel und dafür sollte 0x3FFF reichen. Da finde ich nichts. Ggf. anders DB?
MWS schrieb: > Da finde ich nichts. Ggf. anders DB? Ich verwende die Application Note: http://ams.com/eng/content/view/download/318035 MWS schrieb: > entspricht ein 3fff dem Schreibkommando auf das Winkel-Register, statt > zu lesen und der Rückgabewert ist demgemäß irgendwas. Ich bekomme eben ziemlich sinnvolle Daten, daher weiß ich nicht woher die Störung kommt. Ich habe es auch schon nach dem Application Note versucht und die 4x 16bit Befehle in den DMA Buffer geschrieben, doch es kam das gleiche raus.
Sebastian T. schrieb: > Ich bekomme eben ziemlich sinnvolle Daten In die Leitplanke fahren, ist auch ziemlich auf der Straße :-) Du würdest nicht fragen, wäre alles in Ordnung. Gehst Du denn so wie in der AN vor? Also Read-Bit zu 3fff setzen, Parity daraus berechnen und per SPI senden. Nur ein 3fff rauszuklopfen reicht nicht.
Ich verwende den kleinen Bruder, den AS5040, und der liefert wunderbar stabile Werte. Er hat ein nicht ganz so komplexes Interface wie der 5048, der sollte aber genauso funktionieren, wenn man nur den Winkel will. Ich hab das in SW gemacht, war mir schnell genug. Also: CS auf low, 16 mal mit dem Clock wackeln und dabei nach Timingdiagram MISO einlesen, CS wieder high. Dabei MOSI immer high lassen oder gleich einen Pullup. Die erste Messung nach einem Reset ist Rotte, ab dann läufts. Die untersten 14 Bits sind dann der Winkel, die beiden oberen Parity und Errorflag. Sollte man beide auswerten, um z.B. zu sehen, ob der Magnet passend sitzt. Das sollte mit dem 5048 genauso funktionieren, wenn ich das Datenblatt richtig verstanden habe. MfG Klaus
Klaus schrieb: > Ich verwende den kleinen Bruder, den AS5040, und der liefert wunderbar > stabile Werte. Er hat ein nicht ganz so komplexes Interface wie der > 5048, der sollte aber genauso funktionieren, wenn man nur den Winkel > will. Ich hab das in SW gemacht, war mir schnell genug. > > Also: CS auf low, 16 mal mit dem Clock wackeln und dabei nach > Timingdiagram MISO einlesen, CS wieder high. Dabei MOSI immer high > lassen oder gleich einen Pullup. Die erste Messung nach einem Reset ist > Rotte, ab dann läufts. Die untersten 14 Bits sind dann der Winkel, die > beiden oberen Parity und Errorflag. Sollte man beide auswerten, um z.B. > zu sehen, ob der Magnet passend sitzt. > > Das sollte mit dem 5048 genauso funktionieren, wenn ich das Datenblatt > richtig verstanden habe. > > MfG Klaus Danke, das scheint zu funktionieren. Ich bin mir sicher, das ich das schon vorher so gemacht habe und es hat nicht geklappt, aber jetzt geht es. Grüsse Sebastian
Sebastian T. schrieb: > Danke, das scheint zu funktionieren. Kein Wunder, bei Dauer-High ist auch das Read-Bit gesetzt und so schrieb ich's bereits. Damit es wenigstens weitere Suchende verstehen: Du hast 0x3fff geschickt, das ist die Winkeladresse mit Read=0, somit ein Schreibbefehl, dass da Unsinn rauskommt, war klar. Nur eben Dir nicht. Sebastian T. schrieb: > habe ich über die SPI Schnittstelle einfach ein 0x3FFF geschickt
MWS schrieb: > Du hast 0x3fff geschickt, das ist die Winkeladresse mit Read=0, somit > ein Schreibbefehl, dass da Unsinn rauskommt, war klar. Nur eben Dir > nicht. Ich habe eben auch vorher schon 0xFFFF gesendet und dann mit &=0x3FFF die Parity Bits abgeschnitten und trotzdem nur die Hälfte empfangen. Warum es jetzt geht ist mir noch immer nicht klar, ich habe verschiedene neue Projekte erstelle, welche ich nun durchsuche um den Fehler zu finden.
Sebastian T. schrieb: > Ich habe eben auch vorher schon 0xFFFF gesendet und dann mit &=0x3FFF > die Parity Bits abgeschnitten Du hast keine Parity-Bit*s* abgeschnitten, da gibt es nur eines, Du hast damit das Read-Bit gelöscht und einen Schreib- statt eines Lesebefehls auf das Winkelregister geschickt. Und ein 0xffff entspräche einem Dauer-High, also dem 3 wire mode, der ja offenbar funktioniert, also hätte es auch mit 0xffff geklappt.
Ich versuche nun alle Register zu empfangen, um mit den parity bits die eine Fehlerhafte Übertragung zu erkennen, was etwa alle 100-200 samples vorkommt. Das Problem ist, dass ich irgendwie immer falsche parity bits empfange und daher die Daten, auch wenn Sie korrekt sein mögen, falsch sind. Hier mein Code:
1 | #include "SPI_DMA.h" |
2 | |
3 | uint16_t SPIBufferRX[BUFFER_SIZE] = {0}; |
4 | uint16_t SPIBufferTX[BUFFER_SIZE] = {0}; |
5 | uint16_t Data_Buffer[4] = {0}; |
6 | uint8_t dataReady=1; |
7 | uint8_t transferReady=1; |
8 | uint8_t transferState = 1; |
9 | |
10 | void init_SPI_DMA_All() { |
11 | init_SPI_RCC(); |
12 | init_SPI_GPIO(); |
13 | init_SPI_SPI(); |
14 | init_SPI_DMA(); |
15 | }
|
16 | |
17 | void setTXBuffer(uint8_t s) { |
18 | uint16_t dat; |
19 | switch(s) { |
20 | case 1: |
21 | dat = SPI_CMD_READ | SPI_REG_AGC; |
22 | dat |= spiCalcEvenParity(dat) << 15; |
23 | SPIBufferTX[0]=dat; |
24 | break; |
25 | case 2: |
26 | dat = SPI_CMD_READ | SPI_REG_MAG; |
27 | dat |= spiCalcEvenParity(dat) << 15; |
28 | SPIBufferTX[0]=dat; |
29 | break; |
30 | case 3: |
31 | dat = SPI_CMD_READ | SPI_REG_DATA; |
32 | dat |= spiCalcEvenParity(dat) << 15; |
33 | SPIBufferTX[0]=dat; |
34 | break; |
35 | case 4: |
36 | dat = 0x0000; // NOP command. |
37 | SPIBufferTX[0]=dat; |
38 | break; |
39 | default:
|
40 | break; |
41 | }
|
42 | }
|
43 | |
44 | uint8_t spiCalcEvenParity(uint16_t value) { |
45 | uint8_t cnt = 0; |
46 | uint8_t i; |
47 | for (i = 0; i < 16; i++) { |
48 | if (value & 0x1) cnt++; |
49 | value >>= 1; |
50 | }
|
51 | uint8_t parity = cnt & 0x1; |
52 | return parity; |
53 | }
|
54 | |
55 | void init_SPI_RCC() { |
56 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); |
57 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); |
58 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); |
59 | }
|
60 | |
61 | void init_SPI_GPIO() { |
62 | GPIO_InitTypeDef GPIO_InitStructure; |
63 | |
64 | GPIO_StructInit(&GPIO_InitStructure); |
65 | GPIO_InitStructure.GPIO_Pin = SCK_PIN | MISO_PIN | MOSI_PIN; |
66 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; |
67 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; |
68 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; |
69 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; |
70 | GPIO_Init(SPI_PORT, &GPIO_InitStructure); |
71 | |
72 | GPIO_PinAFConfig(SPI_PORT, GPIO_PinSource3, GPIO_AF_SPI3); |
73 | GPIO_PinAFConfig(SPI_PORT, GPIO_PinSource4, GPIO_AF_SPI3); |
74 | GPIO_PinAFConfig(SPI_PORT, GPIO_PinSource5, GPIO_AF_SPI3); |
75 | |
76 | GPIO_InitStructure.GPIO_Pin = SS_PIN; |
77 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; |
78 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; |
79 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; |
80 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; |
81 | GPIO_Init(SPI_PORT, &GPIO_InitStructure); |
82 | |
83 | GPIO_SetBits(SPI_PORT, SS_PIN); // Set SS pin |
84 | }
|
85 | |
86 | void init_SPI_SPI() { |
87 | SPI_InitTypeDef SPI_InitStructure; |
88 | |
89 | SPI_StructInit(&SPI_InitStructure); |
90 | SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; |
91 | SPI_InitStructure.SPI_Mode = SPI_Mode_Master; |
92 | SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; |
93 | SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; |
94 | SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; |
95 | SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; |
96 | SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; |
97 | SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//MSB |
98 | SPI_InitStructure.SPI_CRCPolynomial = 0; |
99 | SPI_Init(SPI3, &SPI_InitStructure); |
100 | }
|
101 | |
102 | void init_SPI_DMA(void) { |
103 | |
104 | NVIC_InitTypeDef NVIC_InitStructure; |
105 | DMA_InitTypeDef DMA_InitStructure; |
106 | |
107 | DMA_Cmd(DMA_STREAM_TX, DISABLE); |
108 | DMA_Cmd(DMA_STREAM_RX, DISABLE); |
109 | DMA_DeInit(DMA_STREAM_TX); |
110 | DMA_DeInit(DMA_STREAM_RX); |
111 | |
112 | //RX
|
113 | DMA_StructInit(&DMA_InitStructure); |
114 | DMA_InitStructure.DMA_Channel = DMA_CHANNEL; //SPI3 Tx DMA is DMA1/Stream4/Channel0 |
115 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI3->DR); //Set the SPI3 Tx |
116 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPIBufferRX; //Set the memory location |
117 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //Sending data from memory to the peripheral's Tx register |
118 | DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; //Define the number of bytes to send |
119 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Don't increment the peripheral 'memory' |
120 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increment the memory location |
121 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Byte size memory transfers |
122 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //Byte size memory transfers |
123 | DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Normal mode (not circular) |
124 | DMA_InitStructure.DMA_Priority = DMA_Priority_High; //Priority is high to avoid saturating the FIFO since we are in direct mode |
125 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //Operate in 'direct mode' without FIFO |
126 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; |
127 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
128 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
129 | DMA_Init(DMA_STREAM_RX, &DMA_InitStructure); |
130 | DMA_ITConfig(DMA_STREAM_RX, DMA_IT_TC, ENABLE); |
131 | |
132 | //TX
|
133 | DMA_InitStructure.DMA_Channel = DMA_CHANNEL; //SPI3 Tx DMA is DMA1/Stream4/Channel0 |
134 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI3->DR); //Set the SPI3 Tx |
135 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPIBufferTX; //Set the memory location |
136 | DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //Sending data from memory to the peripheral's Tx register |
137 | DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; //Define the number of bytes to send |
138 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Don't increment the peripheral 'memory' |
139 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increment the memory location |
140 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Byte size memory transfers |
141 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //Byte size memory transfers |
142 | DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //Normal mode (not circular) |
143 | DMA_InitStructure.DMA_Priority = DMA_Priority_High; //Priority is high to avoid saturating the FIFO since we are in direct mode |
144 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //Operate in 'direct mode' without FIFO |
145 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; |
146 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; |
147 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; |
148 | DMA_Init(DMA_STREAM_TX, &DMA_InitStructure); |
149 | DMA_ITConfig(DMA_STREAM_TX, DMA_IT_TC, ENABLE); |
150 | |
151 | //Interrupt
|
152 | DMA_ClearITPendingBit(DMA_STREAM_RX, DMA_IT_RX_FLAG); |
153 | NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream0_IRQn; |
154 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; |
155 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; |
156 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; |
157 | NVIC_Init(&NVIC_InitStructure); |
158 | |
159 | DMA_ClearITPendingBit(DMA_STREAM_TX, DMA_IT_TX_FLAG); |
160 | NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn; |
161 | NVIC_Init(&NVIC_InitStructure); |
162 | |
163 | SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); |
164 | SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE); |
165 | SPI_Cmd(SPI3, ENABLE); |
166 | }
|
167 | |
168 | int SPI_DMA_getData() { |
169 | if(transferReady) { |
170 | uint16_t data=Data_Buffer[3]&0x3FFF; |
171 | dataReady=1; |
172 | |
173 | if (((Data_Buffer[1] & 0x4000) || (Data_Buffer[2] & 0x4000) || (Data_Buffer[3] & 0x4000))) { |
174 | return -1; |
175 | } else { |
176 | return data; |
177 | }
|
178 | }
|
179 | return -1; |
180 | }
|
181 | |
182 | void SPI_DMA_continue(uint8_t s) { |
183 | s++; |
184 | if(s<=4) { |
185 | transferState=s; |
186 | setTXBuffer(transferState); |
187 | |
188 | Data_Buffer[s-2]=SPIBufferRX[0]; |
189 | |
190 | DMA_SetCurrDataCounter(DMA_STREAM_RX, BUFFER_SIZE); |
191 | DMA_SetCurrDataCounter(DMA_STREAM_TX, BUFFER_SIZE); |
192 | |
193 | GPIO_WriteBit(SPI_PORT, SS_PIN, RESET); |
194 | DMA_Cmd(DMA_STREAM_TX, ENABLE); |
195 | DMA_Cmd(DMA_STREAM_RX, ENABLE); |
196 | |
197 | SPI_Cmd(SPI3, ENABLE); |
198 | |
199 | } else { |
200 | Data_Buffer[3]=SPIBufferRX[0]; |
201 | transferReady=1; |
202 | }
|
203 | |
204 | }
|
205 | |
206 | void SPI_DMA_start(void) { |
207 | if(transferReady && dataReady) { |
208 | transferReady=0; |
209 | dataReady=0; |
210 | |
211 | transferState=1; |
212 | setTXBuffer(transferState); |
213 | |
214 | while((DMA_GetCmdStatus(DMA_STREAM_TX) == ENABLE) || (DMA_GetCmdStatus(DMA_STREAM_RX) == ENABLE)){ |
215 | |
216 | }
|
217 | |
218 | DMA_SetCurrDataCounter(DMA_STREAM_RX, BUFFER_SIZE); |
219 | DMA_SetCurrDataCounter(DMA_STREAM_TX, BUFFER_SIZE); |
220 | |
221 | GPIO_WriteBit(SPI_PORT, SS_PIN, RESET); |
222 | DMA_Cmd(DMA_STREAM_TX, ENABLE); |
223 | DMA_Cmd(DMA_STREAM_RX, ENABLE); |
224 | |
225 | SPI_Cmd(SPI3, ENABLE); |
226 | }
|
227 | }
|
228 | |
229 | void DMA1_Stream0_IRQHandler(void){ //RX |
230 | if(DMA_GetITStatus(DMA_STREAM_RX, DMA_IT_RX_FLAG)) { |
231 | DMA_ClearITPendingBit(DMA_STREAM_RX, DMA_IT_RX_FLAG); |
232 | |
233 | SPI_Cmd(SPI3, DISABLE); |
234 | GPIO_WriteBit(SPI_PORT, SS_PIN, SET); |
235 | DMA_Cmd(DMA_STREAM_RX, DISABLE); |
236 | DMA_Cmd(DMA_STREAM_TX, DISABLE); |
237 | |
238 | SPI_DMA_continue(transferState); |
239 | |
240 | }
|
241 | }
|
242 | |
243 | void DMA1_Stream5_IRQHandler(void){ //TX |
244 | if(DMA_GetITStatus(DMA_STREAM_TX, DMA_IT_TX_FLAG)) { |
245 | DMA_ClearITPendingBit(DMA_STREAM_TX, DMA_IT_TX_FLAG); |
246 | |
247 | }
|
248 | }
|
Wow! Wenn ich da meinen Code anschaue kriege ich ja richtig Minderwertigkeitskomplexe
1 | unsigned int result = 0; |
2 | |
3 | AS_CLK = 1; |
4 | AS_CS = 0; |
5 | __delay_us(1); |
6 | for (i = 0; i < 16; i++) { |
7 | AS_CLK = 0; |
8 | __delay_us(1); |
9 | AS_CLK = 1; |
10 | __delay_us(1); |
11 | result <<= 1; |
12 | if (AS_DO == 1) { |
13 | result |= 1; |
14 | }
|
15 | }
|
16 | AS_CS = 1; |
Wobei man bei 100ns Cycletime die delay_us je nach Prozessor weglassen oder durch ein Nop() ersetzen kann. Dann ist das Ganze in wenigen µs abgehandelt. MfG Klaus
Klaus schrieb: > Wenn ich da meinen Code anschaue kriege ich ja richtig > Minderwertigkeitskomplexe Da wird aber weder der DMA verwendet noch die parity bit Kontrolle ausgeführt. Wenn ich das ganze ohne DMA und ohne parity bit Kontrolle mache, komme ich auf ähnliches.
Der Parity Check scheint nur für die Werte aus [8192,16384] zu gehen, dann bei kleineren Winkelwerten leaken irgendwelche Daten durch.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.