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 #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
127 /* Clock scaler uses system clock (~64MHz) as source, so prescaler 64 => 1MHz. */
\r
128 EMIOS.MCR.B.GPRE = PWM_PRESCALER - 1;
\r
130 /* Enable eMIOS clock */
\r
131 EMIOS.MCR.B.GPREN = 1;
\r
133 /* Stop channels when in debug mode */
\r
134 EMIOS.MCR.B.FRZ = PWM_FREEZE_ENABLE;
\r
136 /* Use global time base */
\r
137 EMIOS.MCR.B.GTBE = 1;
\r
139 Pwm_ModuleState = PWM_STATE_INITIALIZED;
\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
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
149 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
151 * PWM052: The function Pwm_Init shall disable all notifications.
\r
153 * This is now implemented in the configuration macro.
\r
155 // Pwm_DisableNotification(channel);
\r
158 Irq_AttachIsr2(tid, NULL, EMISOS200_FLAG_F0 + channel);
\r
159 ChannelRuntimeStruct[channel].NotificationRoutine
\r
160 = ConfigPtr->NotificationHandlers[channel_iterator];
\r
165 #if PWM_DEINIT_API==STD_ON
\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
172 Pwm_SetOutputToIdle(Channel);
\r
175 // Set the disable bit for this channel
\r
176 EMIOS.UCDIS.R |= (1 << (31 - Channel));
\r
180 * PWM052: The function Pwm_DeInit shall disable all notifications.
\r
182 #if PWM_NOTIFICATION_SUPPORTED==STD_ON
\r
183 Pwm_DisableNotification(Channel);
\r
187 void Pwm_DeInit() {
\r
188 /* TODO: Implement Pwm_DeInit() */
\r
189 Pwm_ChannelType channel_iterator;
\r
191 Pwm_VALIDATE_INITIALIZED();
\r
193 for (channel_iterator = 0; channel_iterator < PWM_NUMBER_OF_CHANNELS; channel_iterator++) {
\r
194 Pwm_DeInitChannel(channel_iterator);
\r
199 EMIOS.MCR.B.MDIS = 1;
\r
201 Pwm_ModuleState = PWM_STATE_UNINITIALIZED;
\r
205 void Pwm_GetVersionInfo(Std_VersionInfoType* VersionInfo) {
\r
206 /* TODO: Implement Pwm_GetVersionInfo */
\r
210 * PWM083: The function Pwm_SetPeriodAndDuty shall be pre compile time
\r
211 * changeable ON/OFF by the configuration parameter PwmSetPeriodAndDuty.
\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
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
221 uint16 leading_edge_position = (uint16) (((uint32) Period
\r
222 * (uint32) DutyCycle) >> 15);
\r
226 /* Timer instant for leading edge */
\r
227 EMIOS.CH[Channel].CADR.R = leading_edge_position;
\r
229 /* Timer instant for the period to restart */
\r
230 EMIOS.CH[Channel].CBDR.R = Period;
\r
237 * PWM013: The function Pwm_SetDutyCycle shall set the duty cycle of the PWM
\r
240 * @TODO: How to conform with PWM018: "The driver shall forbid the spike on the PWM output signal"?
\r
242 * @param Channel PWM channel to use. 0 <= Channel < PWM_NUMBER_OF_CHANNELS <= 16
\r
243 * @param DutyCycle 0 <= DutyCycle <= 0x8000
\r
245 void Pwm_SetDutyCycle(Pwm_ChannelType Channel, Pwm_DutyCycleType DutyCycle) {
\r
247 uint16 leading_edge_position = (uint16) ((EMIOS.CH[Channel].CBDR.R
\r
248 * (uint32) DutyCycle) >> 15);
\r
251 Pwm_VALIDATE_INITIALIZED();
\r
252 Pwm_VALIDATE_CHANNEL(Channel);
\r
254 /* Timer instant for leading edge */
\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
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
266 if (DutyCycle == Pwm_100_Procent || DutyCycle == Pwm_0_Procent) {
\r
267 EMIOS.CH[Channel].CADR.R = 0;
\r
270 EMIOS.CH[Channel].CADR.R = leading_edge_position;
\r
275 void Pwm_SetOutputToIdle(Pwm_ChannelType Channel) {
\r
276 Pwm_VALIDATE_CHANNEL(Channel);
\r
277 Pwm_VALIDATE_INITIALIZED();
\r
279 /* TODO: Make Pwm_SetOutputToIdle sensitive to PwmIdleState (currently uses PwmPolarity) */
\r
280 EMIOS.CH[Channel].CADR.R = 0;
\r
284 * PWM085: The function Pwm_GetOutputState shall be pre compile configurable
\r
285 * ON/OFF by the configuration parameter PwmGetOutputState
\r
287 #if PWM_GET_OUTPUT_STATE==STD_ON
\r
289 * PWM022: The function Pwm_GetOutputState shall read the internal state
\r
290 * of the PWM output signal and return it.
\r
292 Pwm_OutputStateType Pwm_GetOutputState(Pwm_ChannelType Channel) {
\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
301 * Accordingly to PWM025, we should return PWM_LOW on failure.
\r
305 } else if (Pwm_ModuleState != PWM_STATE_INITIALIZED) {
\r
306 Pwm_ReportError(PWM_E_UNINIT);
\r
309 * Accordingly to PWM025, we should return PWM_LOW on failure.
\r
315 return EMIOS.CH[Channel].CSR.B.UCOUT;
\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
325 // Disable flags on this channel
\r
326 EMIOS.CH[Channel].CCR.B.FEN = 0;
\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
334 ChannelRuntimeStruct[Channel].NotificationState = Notification;
\r
336 // Enable flags on this channel
\r
337 EMIOS.CH[Channel].CCR.B.FEN = 1;
\r
340 static void Pwm_Isr(void) {
\r
341 // Find out which channel that triggered the interrupt
\r
343 uint32_t flagmask = EMIOS.GFLAG.R;
\r
344 #elif defined(CFG_MPC5567)
\r
345 uint32_t flagmask = EMIOS.GFR.R;
\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
353 if (ChannelRuntimeStruct[emios_ch].NotificationRoutine != NULL && EMIOS.CH[emios_ch].CCR.B.FEN) {
\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
363 EMIOS.CH[emios_ch].CSR.B.FLAG = 1;
\r
368 #endif /* PWM_NOTIFICATION_SUPPORED == STD_ON */
\r