]> rtime.felk.cvut.cz Git - arc.git/blob - arch/ppc/mpc55xx/drivers/Pwm.c
161aa5a03d6663fabf148c92df522d1cf583e212
[arc.git] / arch / ppc / mpc55xx / drivers / Pwm.c
1 /* -------------------------------- Arctic Core ------------------------------\r
2  * Arctic Core - the open source AUTOSAR platform http://arccore.com\r
3  *\r
4  * Copyright (C) 2009  ArcCore AB <contact@arccore.com>\r
5  *\r
6  * This source code is free software; you can redistribute it and/or modify it\r
7  * under the terms of the GNU General Public License version 2 as published by the\r
8  * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.\r
9  *\r
10  * This program is distributed in the hope that it will be useful, but\r
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\r
13  * for more details.\r
14  * -------------------------------- Arctic Core ------------------------------*/\r
15 \r
16 \r
17 \r
18 \r
19 \r
20 \r
21 \r
22 \r
23 /*\r
24  * Pwm.c\r
25  *\r
26  * TODO: Implement DMA support for PWM\r
27  * TODO: Test PWM for MPC5567 and MPC5554\r
28  *\r
29  *  Created on: 2009-jul-09\r
30  *      Author: nian\r
31  */\r
32 \r
33 //#define USE_KERNEL\r
34 \r
35 #include <assert.h>\r
36 #include <string.h>\r
37 \r
38 #include "Pwm.h"\r
39 #include "MemMap.h"\r
40 //#include "SchM_Pwm.h"\r
41 #include "Det.h"\r
42 #ifdef CFG_MPC5516\r
43 #include "mpc5516.h"\r
44 #elif defined(CFG_MPC5567)\r
45 #include "mpc5567.h"\r
46 #endif\r
47 #if defined(USE_KERNEL)\r
48 #include "Os.h"\r
49 #endif\r
50 #include "Mcu.h"\r
51 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
52 #include "irq.h"\r
53 #include "arc.h"\r
54 #endif\r
55 \r
56 \r
57 #if PWM_DEV_EROR_DETECT==STD_ON\r
58         #define PWM_VALIDATE(_exp, _errid) \\r
59                 if (!(_exp)) { \\r
60                         Pwm_ReportError(_errid); \\r
61                         return; \\r
62                 }\r
63         #define Pwm_VALIDATE_CHANNEL(_ch) PWM_VALIDATE(_ch <= 15, PWM_E_PARAM_CHANNEL)\r
64         #define Pwm_VALIDATE_INITIALIZED() PWM_VALIDATE(Pwm_ModuleState == PWM_STATE_INITIALIZED, PWM_E_UNINIT)\r
65         #define Pwm_VALIDATE_UNINITIALIZED() PWM_VALIDATE(Pwm_ModuleState != PWM_STATE_INITIALIZED, PWM_E_ALREADY_INITIALIZED)\r
66 #else\r
67         #define Pwm_VALIDATE_CHANNEL(ch)\r
68         #define Pwm_VALIDATE_INITIALIZED()\r
69         #define Pwm_VALIDATE_UNINITIALIZED()\r
70 #endif\r
71 \r
72 \r
73 typedef enum {\r
74         PWM_STATE_UNINITIALIZED, PWM_STATE_INITIALIZED\r
75 } Pwm_ModuleStateType;\r
76 \r
77 static Pwm_ModuleStateType Pwm_ModuleState = PWM_STATE_UNINITIALIZED;\r
78 \r
79 // Run-time variables\r
80 typedef struct {\r
81         Pwm_ChannelClassType Class;\r
82 \r
83         #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
84                 Pwm_NotificationHandlerType NotificationRoutine;\r
85                 Pwm_EdgeNotificationType NotificationState;\r
86         #endif\r
87 } Pwm_ChannelStructType;\r
88 \r
89 // We use Pwm_ChannelType as index here\r
90 Pwm_ChannelStructType ChannelRuntimeStruct[16];\r
91 \r
92 /* Local functions */\r
93 void inline Pwm_InitChannel(Pwm_ChannelType Channel);\r
94 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel);\r
95 \r
96 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
97 static void Pwm_Isr(void);\r
98 #endif\r
99 \r
100 void Pwm_Init(const Pwm_ConfigType* ConfigPtr) {\r
101     Pwm_ChannelType channel_iterator;\r
102 \r
103     Pwm_VALIDATE_UNINITIALIZED();\r
104     #if PWM_DEV_EROR_DETECT==STD_ON\r
105         /*\r
106          * PWM046: If development error detection is enabled for the Pwm module,\r
107          * the function Pwm_Init shall raise development error PWM_E_PARAM_CONFIG\r
108          * if ConfigPtr is a null pointer.\r
109          *\r
110          * PWM120: For pre-compile and link-time configuration variants, a NULL\r
111          * pointer shall be passed to the initialization routine. In this case the\r
112          * check for this NULL pointer has to be omitted.\r
113          */\r
114         #if PWM_STATICALLY_CONFIGURED==OFF\r
115             if (ConfigPtr == NULL) {\r
116                 Pwm_ReportError(PWM_E_PARAM_CONFIG);\r
117                 return;\r
118             }\r
119         #endif\r
120     #endif\r
121 \r
122     #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
123         // Create a task for our interrupt service routine.\r
124         TaskType tid = Os_Arc_CreateIsr(Pwm_Isr, PWM_ISR_PRIORITY /*prio*/, "PwmIsr");\r
125     #endif\r
126 \r
127     /* Clock scaler uses system clock (~64MHz) as source, so prescaler 64 => 1MHz. */\r
128     EMIOS.MCR.B.GPRE = PWM_PRESCALER - 1;\r
129 \r
130     /* Enable eMIOS clock */\r
131     EMIOS.MCR.B.GPREN = 1;\r
132 \r
133     /* Stop channels when in debug mode */\r
134     EMIOS.MCR.B.FRZ = PWM_FREEZE_ENABLE;\r
135 \r
136     /* Use global time base */\r
137     EMIOS.MCR.B.GTBE = 1;\r
138 \r
139     Pwm_ModuleState = PWM_STATE_INITIALIZED;\r
140 \r
141     for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {\r
142         Pwm_ChannelType channel = ConfigPtr->Channels[channel_iterator].channel;\r
143 \r
144         // Set up the registers in hw\r
145         memcpy((void*) &EMIOS.CH[channel],\r
146                 (void*) &ConfigPtr->Channels[channel_iterator].r,\r
147                 sizeof(Pwm_ChannelRegisterType));\r
148 \r
149         #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
150                 /*\r
151                  * PWM052: The function Pwm_Init shall disable all notifications.\r
152                  *\r
153                  * This is now implemented in the configuration macro.\r
154                  */\r
155                 // Pwm_DisableNotification(channel);\r
156 \r
157                 // Install ISR\r
158                 Irq_AttachIsr2(tid, NULL, EMISOS200_FLAG_F0 + channel);\r
159                 ChannelRuntimeStruct[channel].NotificationRoutine\r
160                         = ConfigPtr->NotificationHandlers[channel_iterator];\r
161         #endif\r
162     }\r
163 }\r
164 \r
165 #if PWM_DEINIT_API==STD_ON\r
166 \r
167 // TODO: Test that this function in fact turns the channel off.\r
168 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel) {\r
169         Pwm_VALIDATE_CHANNEL(Channel);\r
170         Pwm_VALIDATE_INITIALIZED();\r
171 \r
172     Pwm_SetOutputToIdle(Channel);\r
173 \r
174     #ifdef CFG_MPC5516\r
175         // Set the disable bit for this channel\r
176         EMIOS.UCDIS.R |= (1 << (31 - Channel));\r
177     #endif\r
178 \r
179     /*\r
180      * PWM052: The function Pwm_DeInit shall disable all notifications.\r
181      */\r
182     #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
183         Pwm_DisableNotification(Channel);\r
184     #endif\r
185 }\r
186 \r
187 void Pwm_DeInit() {\r
188         /* TODO: Implement Pwm_DeInit() */\r
189         Pwm_ChannelType channel_iterator;\r
190 \r
191         Pwm_VALIDATE_INITIALIZED();\r
192 \r
193         for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {\r
194                 Pwm_DeInitChannel(channel_iterator);\r
195 \r
196         }\r
197 \r
198         // Disable module\r
199         EMIOS.MCR.B.MDIS = 1;\r
200 \r
201         Pwm_ModuleState = PWM_STATE_UNINITIALIZED;\r
202 }\r
203 #endif\r
204 \r
205 void Pwm_GetVersionInfo(Std_VersionInfoType* VersionInfo) {\r
206         /* TODO: Implement Pwm_GetVersionInfo */\r
207 }\r
208 \r
209 /*\r
210  * PWM083: The function Pwm_SetPeriodAndDuty shall be pre compile time\r
211  * changeable ON/OFF by the configuration parameter PwmSetPeriodAndDuty.\r
212  */\r
213 #if PWM_SET_PERIOD_AND_DUTY==STD_ON\r
214         void Pwm_SetPeriodAndDuty(Pwm_ChannelType Channel, Pwm_PeriodType Period,\r
215                         Pwm_DutyCycleType DutyCycle) {\r
216 \r
217                 Pwm_VALIDATE_INITIALIZED();\r
218                 Pwm_VALIDATE_CHANNEL(Channel);\r
219                 PWM_VALIDATE(ChannelRuntimeStruct[Channel].Class == PWM_VARIABLE_PERIOD, PWM_E_PERIOD_UNCHANGEABLE);\r
220 \r
221                 uint16 leading_edge_position = (uint16) (((uint32) Period\r
222                                 * (uint32) DutyCycle) >> 15);\r
223 \r
224 \r
225 \r
226                 /* Timer instant for leading edge */\r
227                 EMIOS.CH[Channel].CADR.R = leading_edge_position;\r
228 \r
229                 /* Timer instant for the period to restart */\r
230                 EMIOS.CH[Channel].CBDR.R = Period;\r
231 \r
232         }\r
233 #endif\r
234 \r
235 \r
236 /**\r
237  * PWM013: The function Pwm_SetDutyCycle shall set the duty cycle of the PWM\r
238  * channel.\r
239  *\r
240  * @TODO: How to conform with PWM018: "The driver shall forbid the spike on the PWM output signal"?\r
241  *\r
242  * @param Channel PWM channel to use. 0 <= Channel < PWM_NUMBER_OF_CHANNELS <= 16\r
243  * @param DutyCycle 0 <= DutyCycle <= 0x8000\r
244  */\r
245 void Pwm_SetDutyCycle(Pwm_ChannelType Channel, Pwm_DutyCycleType DutyCycle) {\r
246 \r
247         uint16 leading_edge_position = (uint16) ((EMIOS.CH[Channel].CBDR.R\r
248                                 * (uint32) DutyCycle) >> 15);\r
249 \r
250 \r
251         Pwm_VALIDATE_INITIALIZED();\r
252         Pwm_VALIDATE_CHANNEL(Channel);\r
253 \r
254         /* Timer instant for leading edge */\r
255 \r
256         /*\r
257          * PWM017: The function Pwm_SetDutyCycle shall update the duty cycle at\r
258          * the end of the period if supported by the implementation and configured\r
259          * with PwmDutycycleUpdatedEndperiod. [ This is achieved in hardware since\r
260          * the A and B registers are double buffered ]\r
261          *\r
262          * PWM014: The function Pwm_SetDutyCycle shall set the output state according\r
263          * to the configured polarity parameter [which is already set from\r
264          * Pwm_InitChannel], when the duty parameter is 0% [=0] or 100% [=0x8000].\r
265          */\r
266         if (DutyCycle == Pwm_100_Procent || DutyCycle == Pwm_0_Procent) {\r
267                 EMIOS.CH[Channel].CADR.R = 0;\r
268 \r
269         } else {\r
270                 EMIOS.CH[Channel].CADR.R = leading_edge_position;\r
271 \r
272         }\r
273 }\r
274 \r
275 void Pwm_SetOutputToIdle(Pwm_ChannelType Channel) {\r
276         Pwm_VALIDATE_CHANNEL(Channel);\r
277         Pwm_VALIDATE_INITIALIZED();\r
278 \r
279         /* TODO: Make Pwm_SetOutputToIdle sensitive to PwmIdleState (currently uses PwmPolarity) */\r
280         EMIOS.CH[Channel].CADR.R = 0;\r
281 }\r
282 \r
283 /*\r
284  * PWM085: The function Pwm_GetOutputState shall be pre compile configurable\r
285  * ON/OFF by the configuration parameter PwmGetOutputState\r
286  */\r
287 #if PWM_GET_OUTPUT_STATE==STD_ON\r
288         /*\r
289          * PWM022: The function Pwm_GetOutputState shall read the internal state\r
290          * of the PWM output signal and return it.\r
291          */\r
292         Pwm_OutputStateType Pwm_GetOutputState(Pwm_ChannelType Channel) {\r
293 \r
294 \r
295 \r
296                 // We need to return something, even in presence of errors\r
297                 if (Channel >= 16) {\r
298                         Pwm_ReportError(PWM_E_PARAM_CHANNEL);\r
299 \r
300                         /*\r
301                          * Accordingly to PWM025, we should return PWM_LOW on failure.\r
302                          */\r
303                         return PWM_LOW;\r
304 \r
305                 } else if (Pwm_ModuleState != PWM_STATE_INITIALIZED) {\r
306                         Pwm_ReportError(PWM_E_UNINIT);\r
307 \r
308                         /*\r
309                          * Accordingly to PWM025, we should return PWM_LOW on failure.\r
310                          */\r
311                         return PWM_LOW;\r
312 \r
313                 }\r
314 \r
315                 return EMIOS.CH[Channel].CSR.B.UCOUT;\r
316 \r
317         }\r
318 #endif\r
319 \r
320 #if PWM_NOTIFICATION_SUPPORTED==STD_ON\r
321         void Pwm_DisableNotification(Pwm_ChannelType Channel) {\r
322                 Pwm_VALIDATE_CHANNEL(Channel);\r
323                 Pwm_VALIDATE_INITIALIZED();\r
324 \r
325                 // Disable flags on this channel\r
326                 EMIOS.CH[Channel].CCR.B.FEN = 0;\r
327         }\r
328 \r
329         void Pwm_EnableNotification(Pwm_ChannelType Channel,\r
330                         Pwm_EdgeNotificationType Notification) {\r
331                 Pwm_VALIDATE_CHANNEL(Channel);\r
332                 Pwm_VALIDATE_INITIALIZED();\r
333 \r
334                 ChannelRuntimeStruct[Channel].NotificationState = Notification;\r
335 \r
336                 // Enable flags on this channel\r
337                 EMIOS.CH[Channel].CCR.B.FEN = 1;\r
338         }\r
339 \r
340         static void Pwm_Isr(void) {\r
341                 // Find out which channel that triggered the interrupt\r
342 #ifdef CFG_MPC5516\r
343                 uint32_t flagmask = EMIOS.GFLAG.R;\r
344 #elif defined(CFG_MPC5567)\r
345                 uint32_t flagmask = EMIOS.GFR.R;\r
346 #endif\r
347 \r
348                 // There are 24 channels specified in the global flag register, but\r
349                 // we only listen to the first 16 as only these support OPWM\r
350                 for (Pwm_ChannelType emios_ch = 0; emios_ch < 16; emios_ch++) {\r
351                         if (flagmask & (1 << emios_ch)) {\r
352 \r
353                                 if (ChannelRuntimeStruct[emios_ch].NotificationRoutine != NULL && EMIOS.CH[emios_ch].CCR.B.FEN) {\r
354 \r
355                                         Pwm_EdgeNotificationType notification = ChannelRuntimeStruct[emios_ch].NotificationState;\r
356                                         if (notification == PWM_BOTH_EDGES ||\r
357                                                         notification == EMIOS.CH[emios_ch].CSR.B.UCOUT) {\r
358                                                         ChannelRuntimeStruct[emios_ch].NotificationRoutine();\r
359                                         }\r
360                                 }\r
361 \r
362                                 // Clear interrupt\r
363                                 EMIOS.CH[emios_ch].CSR.B.FLAG = 1;\r
364                         }\r
365                 }\r
366         }\r
367 \r
368 #endif /* PWM_NOTIFICATION_SUPPORED == STD_ON */\r