1 /* -------------------------------- Arctic Core ------------------------------
\r
2 * Arctic Core - the open source AUTOSAR platform http://arccore.com
\r
4 * Copyright (C) 2009 ArcCore AB <contact@arccore.com>
\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
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
14 * -------------------------------- Arctic Core ------------------------------*/
\r
26 * TODO: Implement DMA support for PWM
\r
27 * TODO: Test PWM for MPC5567 and MPC5554
\r
29 * Created on: 2009-jul-09
\r
33 //#define USE_KERNEL
\r
40 //#include "SchM_Pwm.h"
\r
43 #include "mpc5516.h"
\r
44 #elif defined(CFG_MPC5567)
\r
45 #include "mpc5567.h"
\r
47 #if defined(USE_KERNEL)
\r
51 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
57 #if PWM_DEV_EROR_DETECT==STD_ON
\r
58 #define PWM_VALIDATE(_exp, _errid) \
\r
60 Pwm_ReportError(_errid); \
\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
67 #define Pwm_VALIDATE_CHANNEL(ch)
\r
68 #define Pwm_VALIDATE_INITIALIZED()
\r
69 #define Pwm_VALIDATE_UNINITIALIZED()
\r
74 PWM_STATE_UNINITIALIZED, PWM_STATE_INITIALIZED
\r
75 } Pwm_ModuleStateType;
\r
77 static Pwm_ModuleStateType Pwm_ModuleState = PWM_STATE_UNINITIALIZED;
\r
79 // Run-time variables
\r
81 Pwm_ChannelClassType Class;
\r
83 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
84 Pwm_NotificationHandlerType NotificationRoutine;
\r
85 Pwm_EdgeNotificationType NotificationState;
\r
87 } Pwm_ChannelStructType;
\r
89 // We use Pwm_ChannelType as index here
\r
90 Pwm_ChannelStructType ChannelRuntimeStruct[16];
\r
92 /* Local functions */
\r
93 void inline Pwm_InitChannel(Pwm_ChannelType Channel);
\r
94 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel);
\r
96 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
97 static void Pwm_Isr(void);
\r
100 void Pwm_Init(const Pwm_ConfigType* ConfigPtr) {
\r
101 Pwm_ChannelType channel_iterator;
\r
103 Pwm_VALIDATE_UNINITIALIZED();
\r
104 #if PWM_DEV_EROR_DETECT==STD_ON
\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
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
114 #if PWM_STATICALLY_CONFIGURED==OFF
\r
115 if (ConfigPtr == NULL) {
\r
116 Pwm_ReportError(PWM_E_PARAM_CONFIG);
\r
122 /* Clock scaler uses system clock (~64MHz) as source, so prescaler 64 => 1MHz. */
\r
123 EMIOS.MCR.B.GPRE = PWM_PRESCALER - 1;
\r
125 /* Enable eMIOS clock */
\r
126 EMIOS.MCR.B.GPREN = 1;
\r
128 /* Stop channels when in debug mode */
\r
129 EMIOS.MCR.B.FRZ = PWM_FREEZE_ENABLE;
\r
131 /* Use global time base */
\r
132 EMIOS.MCR.B.GTBE = 1;
\r
134 Pwm_ModuleState = PWM_STATE_INITIALIZED;
\r
136 for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {
\r
137 Pwm_ChannelType channel = ConfigPtr->Channels[channel_iterator].channel;
\r
139 // Set up the registers in hw
\r
140 memcpy((void*) &EMIOS.CH[channel],
\r
141 (void*) &ConfigPtr->Channels[channel_iterator].r,
\r
142 sizeof(Pwm_ChannelRegisterType));
\r
144 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
146 * PWM052: The function Pwm_Init shall disable all notifications.
\r
148 * This is now implemented in the configuration macro.
\r
150 // Pwm_DisableNotification(channel);
\r
154 case 0x0: ISR_INSTALL_ISR2( "Pwm0", Pwm_Isr, EMISOS200_FLAG_F0, PWM_ISR_PRIORITY, 0 ); break;
\r
155 case 0x1: ISR_INSTALL_ISR2( "Pwm1", Pwm_Isr, EMISOS200_FLAG_F1, PWM_ISR_PRIORITY, 0 ); break;
\r
156 case 0x2: ISR_INSTALL_ISR2( "Pwm2", Pwm_Isr, EMISOS200_FLAG_F2, PWM_ISR_PRIORITY, 0 ); break;
\r
157 case 0x3: ISR_INSTALL_ISR2( "Pwm3", Pwm_Isr, EMISOS200_FLAG_F3, PWM_ISR_PRIORITY, 0 ); break;
\r
158 case 0x4: ISR_INSTALL_ISR2( "Pwm4", Pwm_Isr, EMISOS200_FLAG_F4, PWM_ISR_PRIORITY, 0 ); break;
\r
159 case 0x5: ISR_INSTALL_ISR2( "Pwm5", Pwm_Isr, EMISOS200_FLAG_F5, PWM_ISR_PRIORITY, 0 ); break;
\r
160 case 0x6: ISR_INSTALL_ISR2( "Pwm6", Pwm_Isr, EMISOS200_FLAG_F6, PWM_ISR_PRIORITY, 0 ); break;
\r
161 case 0x7: ISR_INSTALL_ISR2( "Pwm7", Pwm_Isr, EMISOS200_FLAG_F7, PWM_ISR_PRIORITY, 0 ); break;
\r
162 case 0x8: ISR_INSTALL_ISR2( "Pwm8", Pwm_Isr, EMISOS200_FLAG_F8, PWM_ISR_PRIORITY, 0 ); break;
\r
163 case 0x9: ISR_INSTALL_ISR2( "Pwm9", Pwm_Isr, EMISOS200_FLAG_F9, PWM_ISR_PRIORITY, 0 ); break;
\r
164 case 0xA: ISR_INSTALL_ISR2( "Pwm10", Pwm_Isr, EMISOS200_FLAG_F10, PWM_ISR_PRIORITY, 0 ); break;
\r
165 case 0xB: ISR_INSTALL_ISR2( "Pwm11", Pwm_Isr, EMISOS200_FLAG_F11, PWM_ISR_PRIORITY, 0 ); break;
\r
166 case 0xC: ISR_INSTALL_ISR2( "Pwm12", Pwm_Isr, EMISOS200_FLAG_F12, PWM_ISR_PRIORITY, 0 ); break;
\r
167 case 0xD: ISR_INSTALL_ISR2( "Pwm13", Pwm_Isr, EMISOS200_FLAG_F13, PWM_ISR_PRIORITY, 0 ); break;
\r
168 case 0xE: ISR_INSTALL_ISR2( "Pwm14", Pwm_Isr, EMISOS200_FLAG_F14, PWM_ISR_PRIORITY, 0 ); break;
\r
169 case 0xF: ISR_INSTALL_ISR2( "Pwm15", Pwm_Isr, EMISOS200_FLAG_F15, PWM_ISR_PRIORITY, 0 ); break;
\r
170 default: assert(0); break;
\r
173 ChannelRuntimeStruct[channel].NotificationRoutine
\r
174 = ConfigPtr->NotificationHandlers[channel_iterator];
\r
179 #if PWM_DEINIT_API==STD_ON
\r
181 // TODO: Test that this function in fact turns the channel off.
\r
182 void inline Pwm_DeInitChannel(Pwm_ChannelType Channel) {
\r
183 Pwm_VALIDATE_CHANNEL(Channel);
\r
184 Pwm_VALIDATE_INITIALIZED();
\r
186 Pwm_SetOutputToIdle(Channel);
\r
189 // Set the disable bit for this channel
\r
190 EMIOS.UCDIS.R |= (1 << (31 - Channel));
\r
194 * PWM052: The function Pwm_DeInit shall disable all notifications.
\r
196 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
197 Pwm_DisableNotification(Channel);
\r
201 void Pwm_DeInit() {
\r
202 /* TODO: Implement Pwm_DeInit() */
\r
203 Pwm_ChannelType channel_iterator;
\r
205 Pwm_VALIDATE_INITIALIZED();
\r
207 for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {
\r
208 Pwm_DeInitChannel(channel_iterator);
\r
213 EMIOS.MCR.B.MDIS = 1;
\r
215 Pwm_ModuleState = PWM_STATE_UNINITIALIZED;
\r
219 void Pwm_GetVersionInfo(Std_VersionInfoType* VersionInfo) {
\r
220 /* TODO: Implement Pwm_GetVersionInfo */
\r
224 * PWM083: The function Pwm_SetPeriodAndDuty shall be pre compile time
\r
225 * changeable ON/OFF by the configuration parameter PwmSetPeriodAndDuty.
\r
227 #if PWM_SET_PERIOD_AND_DUTY==STD_ON
\r
228 void Pwm_SetPeriodAndDuty(Pwm_ChannelType Channel, Pwm_PeriodType Period,
\r
229 Pwm_DutyCycleType DutyCycle) {
\r
231 Pwm_VALIDATE_INITIALIZED();
\r
232 Pwm_VALIDATE_CHANNEL(Channel);
\r
233 PWM_VALIDATE(ChannelRuntimeStruct[Channel].Class == PWM_VARIABLE_PERIOD, PWM_E_PERIOD_UNCHANGEABLE);
\r
235 uint16 leading_edge_position = (uint16) (((uint32) Period
\r
236 * (uint32) DutyCycle) >> 15);
\r
240 /* Timer instant for leading edge */
\r
241 EMIOS.CH[Channel].CADR.R = leading_edge_position;
\r
243 /* Timer instant for the period to restart */
\r
244 EMIOS.CH[Channel].CBDR.R = Period;
\r
251 * PWM013: The function Pwm_SetDutyCycle shall set the duty cycle of the PWM
\r
254 * @TODO: How to conform with PWM018: "The driver shall forbid the spike on the PWM output signal"?
\r
256 * @param Channel PWM channel to use. 0 <= Channel < PWM_NUMBER_OF_CHANNELS <= 16
\r
257 * @param DutyCycle 0 <= DutyCycle <= 0x8000
\r
259 void Pwm_SetDutyCycle(Pwm_ChannelType Channel, Pwm_DutyCycleType DutyCycle) {
\r
261 uint16 leading_edge_position = (uint16) ((EMIOS.CH[Channel].CBDR.R
\r
262 * (uint32) DutyCycle) >> 15);
\r
265 Pwm_VALIDATE_INITIALIZED();
\r
266 Pwm_VALIDATE_CHANNEL(Channel);
\r
268 /* Timer instant for leading edge */
\r
271 * PWM017: The function Pwm_SetDutyCycle shall update the duty cycle at
\r
272 * the end of the period if supported by the implementation and configured
\r
273 * with PwmDutycycleUpdatedEndperiod. [ This is achieved in hardware since
\r
274 * the A and B registers are double buffered ]
\r
276 * PWM014: The function Pwm_SetDutyCycle shall set the output state according
\r
277 * to the configured polarity parameter [which is already set from
\r
278 * Pwm_InitChannel], when the duty parameter is 0% [=0] or 100% [=0x8000].
\r
280 if (DutyCycle == Pwm_100_Procent || DutyCycle == Pwm_0_Procent) {
\r
281 EMIOS.CH[Channel].CADR.R = 0;
\r
284 EMIOS.CH[Channel].CADR.R = leading_edge_position;
\r
289 void Pwm_SetOutputToIdle(Pwm_ChannelType Channel) {
\r
290 Pwm_VALIDATE_CHANNEL(Channel);
\r
291 Pwm_VALIDATE_INITIALIZED();
\r
293 /* TODO: Make Pwm_SetOutputToIdle sensitive to PwmIdleState (currently uses PwmPolarity) */
\r
294 EMIOS.CH[Channel].CADR.R = 0;
\r
298 * PWM085: The function Pwm_GetOutputState shall be pre compile configurable
\r
299 * ON/OFF by the configuration parameter PwmGetOutputState
\r
301 #if PWM_GET_OUTPUT_STATE==STD_ON
\r
303 * PWM022: The function Pwm_GetOutputState shall read the internal state
\r
304 * of the PWM output signal and return it.
\r
306 Pwm_OutputStateType Pwm_GetOutputState(Pwm_ChannelType Channel) {
\r
310 // We need to return something, even in presence of errors
\r
311 if (Channel >= 16) {
\r
312 Pwm_ReportError(PWM_E_PARAM_CHANNEL);
\r
315 * Accordingly to PWM025, we should return PWM_LOW on failure.
\r
319 } else if (Pwm_ModuleState != PWM_STATE_INITIALIZED) {
\r
320 Pwm_ReportError(PWM_E_UNINIT);
\r
323 * Accordingly to PWM025, we should return PWM_LOW on failure.
\r
329 return EMIOS.CH[Channel].CSR.B.UCOUT;
\r
334 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
335 void Pwm_DisableNotification(Pwm_ChannelType Channel) {
\r
336 Pwm_VALIDATE_CHANNEL(Channel);
\r
337 Pwm_VALIDATE_INITIALIZED();
\r
339 // Disable flags on this channel
\r
340 EMIOS.CH[Channel].CCR.B.FEN = 0;
\r
343 void Pwm_EnableNotification(Pwm_ChannelType Channel,
\r
344 Pwm_EdgeNotificationType Notification) {
\r
345 Pwm_VALIDATE_CHANNEL(Channel);
\r
346 Pwm_VALIDATE_INITIALIZED();
\r
348 ChannelRuntimeStruct[Channel].NotificationState = Notification;
\r
350 // Enable flags on this channel
\r
351 EMIOS.CH[Channel].CCR.B.FEN = 1;
\r
354 static void Pwm_Isr(void) {
\r
355 // Find out which channel that triggered the interrupt
\r
357 uint32_t flagmask = EMIOS.GFLAG.R;
\r
358 #elif defined(CFG_MPC5567)
\r
359 uint32_t flagmask = EMIOS.GFR.R;
\r
362 // There are 24 channels specified in the global flag register, but
\r
363 // we only listen to the first 16 as only these support OPWM
\r
364 for (Pwm_ChannelType emios_ch = 0; emios_ch < 16; emios_ch++) {
\r
365 if (flagmask & (1 << emios_ch)) {
\r
367 if (ChannelRuntimeStruct[emios_ch].NotificationRoutine != NULL && EMIOS.CH[emios_ch].CCR.B.FEN) {
\r
369 Pwm_EdgeNotificationType notification = ChannelRuntimeStruct[emios_ch].NotificationState;
\r
370 if (notification == PWM_BOTH_EDGES ||
\r
371 notification == EMIOS.CH[emios_ch].CSR.B.UCOUT) {
\r
372 ChannelRuntimeStruct[emios_ch].NotificationRoutine();
\r
377 EMIOS.CH[emios_ch].CSR.B.FLAG = 1;
\r
382 #endif /* PWM_NOTIFICATION_SUPPORED == STD_ON */
\r