Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

936 linhas
35 KiB

  1. /*
  2. * Copyright (c) 2015, Freescale Semiconductor, Inc.
  3. * Copyright 2016-2020 NXP
  4. * All rights reserved.
  5. *
  6. * SPDX-License-Identifier: BSD-3-Clause
  7. */
  8. #include "fsl_pwm.h"
  9. /* Component ID definition, used by tools. */
  10. #ifndef FSL_COMPONENT_ID
  11. #define FSL_COMPONENT_ID "platform.drivers.pwm"
  12. #endif
  13. /*******************************************************************************
  14. * Prototypes
  15. ******************************************************************************/
  16. /*!
  17. * @brief Get the instance from the base address
  18. *
  19. * @param base PWM peripheral base address
  20. *
  21. * @return The PWM module instance
  22. */
  23. static uint32_t PWM_GetInstance(PWM_Type *base);
  24. /*******************************************************************************
  25. * Variables
  26. ******************************************************************************/
  27. /*! @brief Pointers to PWM bases for each instance. */
  28. static PWM_Type *const s_pwmBases[] = PWM_BASE_PTRS;
  29. #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
  30. /*! @brief Pointers to PWM clocks for each PWM submodule. */
  31. static const clock_ip_name_t s_pwmClocks[][FSL_FEATURE_PWM_SUBMODULE_COUNT] = PWM_CLOCKS;
  32. #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  33. /*******************************************************************************
  34. * Code
  35. ******************************************************************************/
  36. /*!
  37. * brief Complement the variable of type uint16_t as needed
  38. *
  39. * This function can complement the variable of type uint16_t as needed.For example,
  40. * need to ask for the opposite of a positive integer.
  41. *
  42. * param value Parameters of type uint16_t
  43. */
  44. static inline uint16_t PWM_GetComplementU16(uint16_t value)
  45. {
  46. return (~value + 1U);
  47. }
  48. static inline uint16_t dutyCycleToReloadValue(uint8_t dutyCyclePercent)
  49. {
  50. /* Rounding calculations to improve the accuracy of reloadValue */
  51. return ((65535U * dutyCyclePercent) + 50U) / 100U;
  52. }
  53. static uint32_t PWM_GetInstance(PWM_Type *base)
  54. {
  55. uint32_t instance;
  56. /* Find the instance index from base address mappings. */
  57. for (instance = 0; instance < ARRAY_SIZE(s_pwmBases); instance++)
  58. {
  59. if (s_pwmBases[instance] == base)
  60. {
  61. break;
  62. }
  63. }
  64. assert(instance < ARRAY_SIZE(s_pwmBases));
  65. return instance;
  66. }
  67. /*!
  68. * brief Ungates the PWM submodule clock and configures the peripheral for basic operation.
  69. *
  70. * note This API should be called at the beginning of the application using the PWM driver.
  71. *
  72. * param base PWM peripheral base address
  73. * param subModule PWM submodule to configure
  74. * param config Pointer to user's PWM config structure.
  75. *
  76. * return kStatus_Success means success; else failed.
  77. */
  78. status_t PWM_Init(PWM_Type *base, pwm_submodule_t subModule, const pwm_config_t *config)
  79. {
  80. assert(config);
  81. uint16_t reg;
  82. /* Source clock for submodule 0 cannot be itself */
  83. if ((config->clockSource == kPWM_Submodule0Clock) && (subModule == kPWM_Module_0))
  84. {
  85. return kStatus_Fail;
  86. }
  87. /* Reload source select clock for submodule 0 cannot be master reload */
  88. if ((config->reloadSelect == kPWM_MasterReload) && (subModule == kPWM_Module_0))
  89. {
  90. return kStatus_Fail;
  91. }
  92. #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
  93. /* Ungate the PWM submodule clock*/
  94. CLOCK_EnableClock(s_pwmClocks[PWM_GetInstance(base)][subModule]);
  95. #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  96. /* Clear the fault status flags */
  97. base->FSTS |= PWM_FSTS_FFLAG_MASK;
  98. reg = base->SM[subModule].CTRL2;
  99. /* Setup the submodule clock-source, control source of the INIT signal,
  100. * source of the force output signal, operation in debug & wait modes and reload source select
  101. */
  102. reg &= ~(uint16_t)(PWM_CTRL2_CLK_SEL_MASK | PWM_CTRL2_FORCE_SEL_MASK | PWM_CTRL2_INIT_SEL_MASK |
  103. PWM_CTRL2_INDEP_MASK | PWM_CTRL2_WAITEN_MASK | PWM_CTRL2_DBGEN_MASK | PWM_CTRL2_RELOAD_SEL_MASK);
  104. reg |= (PWM_CTRL2_CLK_SEL(config->clockSource) | PWM_CTRL2_FORCE_SEL(config->forceTrigger) |
  105. PWM_CTRL2_INIT_SEL(config->initializationControl) | PWM_CTRL2_DBGEN(config->enableDebugMode) |
  106. PWM_CTRL2_WAITEN(config->enableWait) | PWM_CTRL2_RELOAD_SEL(config->reloadSelect));
  107. /* Setup PWM A & B to be independent or a complementary-pair */
  108. switch (config->pairOperation)
  109. {
  110. case kPWM_Independent:
  111. reg |= PWM_CTRL2_INDEP_MASK;
  112. break;
  113. case kPWM_ComplementaryPwmA:
  114. base->MCTRL &= ~((uint16_t)1U << (PWM_MCTRL_IPOL_SHIFT + (uint16_t)subModule));
  115. break;
  116. case kPWM_ComplementaryPwmB:
  117. base->MCTRL |= ((uint16_t)1U << (PWM_MCTRL_IPOL_SHIFT + (uint16_t)subModule));
  118. break;
  119. default:
  120. assert(false);
  121. break;
  122. }
  123. base->SM[subModule].CTRL2 = reg;
  124. reg = base->SM[subModule].CTRL;
  125. /* Setup the clock prescale, load mode and frequency */
  126. reg &= ~(uint16_t)(PWM_CTRL_PRSC_MASK | PWM_CTRL_LDFQ_MASK | PWM_CTRL_LDMOD_MASK);
  127. reg |= (PWM_CTRL_PRSC(config->prescale) | PWM_CTRL_LDFQ(config->reloadFrequency));
  128. /* Setup register reload logic */
  129. switch (config->reloadLogic)
  130. {
  131. case kPWM_ReloadImmediate:
  132. reg |= PWM_CTRL_LDMOD_MASK;
  133. break;
  134. case kPWM_ReloadPwmHalfCycle:
  135. reg |= PWM_CTRL_HALF_MASK;
  136. reg &= (uint16_t)(~PWM_CTRL_FULL_MASK);
  137. break;
  138. case kPWM_ReloadPwmFullCycle:
  139. reg &= (uint16_t)(~PWM_CTRL_HALF_MASK);
  140. reg |= PWM_CTRL_FULL_MASK;
  141. break;
  142. case kPWM_ReloadPwmHalfAndFullCycle:
  143. reg |= PWM_CTRL_HALF_MASK;
  144. reg |= PWM_CTRL_FULL_MASK;
  145. break;
  146. default:
  147. assert(false);
  148. break;
  149. }
  150. base->SM[subModule].CTRL = reg;
  151. /* Issue a Force trigger event when configured to trigger locally */
  152. if (config->forceTrigger == kPWM_Force_Local)
  153. {
  154. base->SM[subModule].CTRL2 |= PWM_CTRL2_FORCE(1U);
  155. }
  156. return kStatus_Success;
  157. }
  158. /*!
  159. * brief Gate the PWM submodule clock
  160. *
  161. * param base PWM peripheral base address
  162. * param subModule PWM submodule to deinitialize
  163. */
  164. void PWM_Deinit(PWM_Type *base, pwm_submodule_t subModule)
  165. {
  166. /* Stop the submodule */
  167. base->MCTRL &= ~((uint16_t)1U << (PWM_MCTRL_RUN_SHIFT + (uint16_t)subModule));
  168. #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
  169. /* Gate the PWM submodule clock*/
  170. CLOCK_DisableClock(s_pwmClocks[PWM_GetInstance(base)][subModule]);
  171. #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
  172. }
  173. /*!
  174. * brief Fill in the PWM config struct with the default settings
  175. *
  176. * The default values are:
  177. * code
  178. * config->enableDebugMode = false;
  179. * config->enableWait = false;
  180. * config->reloadSelect = kPWM_LocalReload;
  181. * config->clockSource = kPWM_BusClock;
  182. * config->prescale = kPWM_Prescale_Divide_1;
  183. * config->initializationControl = kPWM_Initialize_LocalSync;
  184. * config->forceTrigger = kPWM_Force_Local;
  185. * config->reloadFrequency = kPWM_LoadEveryOportunity;
  186. * config->reloadLogic = kPWM_ReloadImmediate;
  187. * config->pairOperation = kPWM_Independent;
  188. * endcode
  189. * param config Pointer to user's PWM config structure.
  190. */
  191. void PWM_GetDefaultConfig(pwm_config_t *config)
  192. {
  193. assert(config);
  194. /* Initializes the configure structure to zero. */
  195. (void)memset(config, 0, sizeof(*config));
  196. /* PWM is paused in debug mode */
  197. config->enableDebugMode = false;
  198. /* PWM is paused in wait mode */
  199. config->enableWait = false;
  200. /* PWM module uses the local reload signal to reload registers */
  201. config->reloadSelect = kPWM_LocalReload;
  202. /* Use the IP Bus clock as source clock for the PWM submodule */
  203. config->clockSource = kPWM_BusClock;
  204. /* Clock source prescale is set to divide by 1*/
  205. config->prescale = kPWM_Prescale_Divide_1;
  206. /* Local sync causes initialization */
  207. config->initializationControl = kPWM_Initialize_LocalSync;
  208. /* The local force signal, CTRL2[FORCE], from the submodule is used to force updates */
  209. config->forceTrigger = kPWM_Force_Local;
  210. /* PWM reload frequency, reload opportunity is PWM half cycle or full cycle.
  211. * This field is not used in Immediate reload mode
  212. */
  213. config->reloadFrequency = kPWM_LoadEveryOportunity;
  214. /* Buffered-registers get loaded with new values as soon as LDOK bit is set */
  215. config->reloadLogic = kPWM_ReloadImmediate;
  216. /* PWM A & PWM B operate as 2 independent channels */
  217. config->pairOperation = kPWM_Independent;
  218. }
  219. /*!
  220. * brief Sets up the PWM signals for a PWM submodule.
  221. *
  222. * The function initializes the submodule according to the parameters passed in by the user. The function
  223. * also sets up the value compare registers to match the PWM signal requirements.
  224. * If the dead time insertion logic is enabled, the pulse period is reduced by the
  225. * dead time period specified by the user.
  226. *
  227. * param base PWM peripheral base address
  228. * param subModule PWM submodule to configure
  229. * param chnlParams Array of PWM channel parameters to configure the channel(s)
  230. * param numOfChnls Number of channels to configure, this should be the size of the array passed in.
  231. * Array size should not be more than 2 as each submodule has 2 pins to output PWM
  232. * param mode PWM operation mode, options available in enumeration ::pwm_mode_t
  233. * param pwmFreq_Hz PWM signal frequency in Hz
  234. * param srcClock_Hz PWM main counter clock in Hz.
  235. *
  236. * return Returns kStatusFail if there was error setting up the signal; kStatusSuccess otherwise
  237. */
  238. status_t PWM_SetupPwm(PWM_Type *base,
  239. pwm_submodule_t subModule,
  240. const pwm_signal_param_t *chnlParams,
  241. uint8_t numOfChnls,
  242. pwm_mode_t mode,
  243. uint32_t pwmFreq_Hz,
  244. uint32_t srcClock_Hz)
  245. {
  246. assert(chnlParams);
  247. assert(pwmFreq_Hz);
  248. assert(numOfChnls);
  249. assert(srcClock_Hz);
  250. uint32_t pwmClock;
  251. uint16_t pulseCnt = 0, pwmHighPulse = 0;
  252. uint16_t modulo = 0;
  253. uint8_t i, polarityShift = 0, outputEnableShift = 0;
  254. if (numOfChnls > 2U)
  255. {
  256. /* Each submodule has 2 signals; PWM A & PWM B */
  257. return kStatus_Fail;
  258. }
  259. /* Divide the clock by the prescale value */
  260. pwmClock = (srcClock_Hz / (1UL << ((base->SM[subModule].CTRL & PWM_CTRL_PRSC_MASK) >> PWM_CTRL_PRSC_SHIFT)));
  261. pulseCnt = (uint16_t)(pwmClock / pwmFreq_Hz);
  262. /* Setup each PWM channel */
  263. for (i = 0; i < numOfChnls; i++)
  264. {
  265. /* Calculate pulse width */
  266. pwmHighPulse = (pulseCnt * chnlParams->dutyCyclePercent) / 100U;
  267. /* Setup the different match registers to generate the PWM signal */
  268. switch (mode)
  269. {
  270. case kPWM_SignedCenterAligned:
  271. /* Setup the PWM period for a signed center aligned signal */
  272. if (i == 0U)
  273. {
  274. modulo = (pulseCnt >> 1U);
  275. /* Indicates the start of the PWM period */
  276. base->SM[subModule].INIT = PWM_GetComplementU16(modulo);
  277. /* Indicates the center value */
  278. base->SM[subModule].VAL0 = 0;
  279. /* Indicates the end of the PWM period */
  280. /* The change during the end to start of the PWM period requires a count time */
  281. base->SM[subModule].VAL1 = modulo - 1U;
  282. }
  283. /* Setup the PWM dutycycle */
  284. if (chnlParams->pwmChannel == kPWM_PwmA)
  285. {
  286. base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U);
  287. base->SM[subModule].VAL3 = (pwmHighPulse / 2U);
  288. }
  289. else
  290. {
  291. base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U);
  292. base->SM[subModule].VAL5 = (pwmHighPulse / 2U);
  293. }
  294. break;
  295. case kPWM_CenterAligned:
  296. /* Setup the PWM period for an unsigned center aligned signal */
  297. /* Indicates the start of the PWM period */
  298. if (i == 0U)
  299. {
  300. base->SM[subModule].INIT = 0;
  301. /* Indicates the center value */
  302. base->SM[subModule].VAL0 = (pulseCnt / 2U);
  303. /* Indicates the end of the PWM period */
  304. /* The change during the end to start of the PWM period requires a count time */
  305. base->SM[subModule].VAL1 = pulseCnt - 1U;
  306. }
  307. /* Setup the PWM dutycycle */
  308. if (chnlParams->pwmChannel == kPWM_PwmA)
  309. {
  310. base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U);
  311. base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U);
  312. }
  313. else
  314. {
  315. base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U);
  316. base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U);
  317. }
  318. break;
  319. case kPWM_SignedEdgeAligned:
  320. /* Setup the PWM period for a signed edge aligned signal */
  321. if (i == 0U)
  322. {
  323. modulo = (pulseCnt >> 1U);
  324. /* Indicates the start of the PWM period */
  325. base->SM[subModule].INIT = PWM_GetComplementU16(modulo);
  326. /* Indicates the center value */
  327. base->SM[subModule].VAL0 = 0;
  328. /* Indicates the end of the PWM period */
  329. /* The change during the end to start of the PWM period requires a count time */
  330. base->SM[subModule].VAL1 = modulo - 1U;
  331. }
  332. /* Setup the PWM dutycycle */
  333. if (chnlParams->pwmChannel == kPWM_PwmA)
  334. {
  335. base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo);
  336. base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse;
  337. }
  338. else
  339. {
  340. base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo);
  341. base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse;
  342. }
  343. break;
  344. case kPWM_EdgeAligned:
  345. /* Setup the PWM period for a unsigned edge aligned signal */
  346. /* Indicates the start of the PWM period */
  347. if (i == 0U)
  348. {
  349. base->SM[subModule].INIT = 0;
  350. /* Indicates the center value */
  351. base->SM[subModule].VAL0 = (pulseCnt / 2U);
  352. /* Indicates the end of the PWM period */
  353. /* The change during the end to start of the PWM period requires a count time */
  354. base->SM[subModule].VAL1 = pulseCnt - 1U;
  355. }
  356. /* Setup the PWM dutycycle */
  357. if (chnlParams->pwmChannel == kPWM_PwmA)
  358. {
  359. base->SM[subModule].VAL2 = 0;
  360. base->SM[subModule].VAL3 = pwmHighPulse;
  361. }
  362. else
  363. {
  364. base->SM[subModule].VAL4 = 0;
  365. base->SM[subModule].VAL5 = pwmHighPulse;
  366. }
  367. break;
  368. default:
  369. assert(false);
  370. break;
  371. }
  372. /* Setup register shift values based on the channel being configured.
  373. * Also setup the deadtime value
  374. */
  375. if (chnlParams->pwmChannel == kPWM_PwmA)
  376. {
  377. polarityShift = PWM_OCTRL_POLA_SHIFT;
  378. outputEnableShift = PWM_OUTEN_PWMA_EN_SHIFT;
  379. base->SM[subModule].DTCNT0 = PWM_DTCNT0_DTCNT0(chnlParams->deadtimeValue);
  380. }
  381. else
  382. {
  383. polarityShift = PWM_OCTRL_POLB_SHIFT;
  384. outputEnableShift = PWM_OUTEN_PWMB_EN_SHIFT;
  385. base->SM[subModule].DTCNT1 = PWM_DTCNT1_DTCNT1(chnlParams->deadtimeValue);
  386. }
  387. /* Set PWM output fault status */
  388. switch (chnlParams->pwmChannel)
  389. {
  390. case kPWM_PwmA:
  391. base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMAFS_MASK);
  392. base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMAFS_SHIFT) &
  393. (uint16_t)PWM_OCTRL_PWMAFS_MASK);
  394. break;
  395. case kPWM_PwmB:
  396. base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMBFS_MASK);
  397. base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMBFS_SHIFT) &
  398. (uint16_t)PWM_OCTRL_PWMBFS_MASK);
  399. break;
  400. case kPWM_PwmX:
  401. base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMXFS_MASK);
  402. base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMXFS_SHIFT) &
  403. (uint16_t)PWM_OCTRL_PWMXFS_MASK);
  404. break;
  405. default:
  406. assert(false);
  407. break;
  408. }
  409. /* Setup signal active level */
  410. if ((bool)chnlParams->level == kPWM_HighTrue)
  411. {
  412. base->SM[subModule].OCTRL &= ~((uint16_t)1U << (uint16_t)polarityShift);
  413. }
  414. else
  415. {
  416. base->SM[subModule].OCTRL |= ((uint16_t)1U << (uint16_t)polarityShift);
  417. }
  418. /* Enable PWM output */
  419. base->OUTEN |= ((uint16_t)1U << ((uint16_t)outputEnableShift + (uint16_t)subModule));
  420. /* Get the next channel parameters */
  421. chnlParams++;
  422. }
  423. return kStatus_Success;
  424. }
  425. /*!
  426. * brief Updates the PWM signal's dutycycle.
  427. *
  428. * The function updates the PWM dutycyle to the new value that is passed in.
  429. * If the dead time insertion logic is enabled then the pulse period is reduced by the
  430. * dead time period specified by the user.
  431. *
  432. * param base PWM peripheral base address
  433. * param subModule PWM submodule to configure
  434. * param pwmSignal Signal (PWM A or PWM B) to update
  435. * param currPwmMode The current PWM mode set during PWM setup
  436. * param dutyCyclePercent New PWM pulse width, value should be between 0 to 100
  437. * 0=inactive signal(0% duty cycle)...
  438. * 100=active signal (100% duty cycle)
  439. */
  440. void PWM_UpdatePwmDutycycle(PWM_Type *base,
  441. pwm_submodule_t subModule,
  442. pwm_channels_t pwmSignal,
  443. pwm_mode_t currPwmMode,
  444. uint8_t dutyCyclePercent)
  445. {
  446. assert(dutyCyclePercent <= 100U);
  447. assert((uint16_t)pwmSignal < 2U);
  448. uint16_t reloadValue = dutyCycleToReloadValue(dutyCyclePercent);
  449. PWM_UpdatePwmDutycycleHighAccuracy(base, subModule, pwmSignal, currPwmMode, reloadValue);
  450. }
  451. /*!
  452. * brief Updates the PWM signal's dutycycle with 16-bit accuracy.
  453. *
  454. * The function updates the PWM dutycyle to the new value that is passed in.
  455. * If the dead time insertion logic is enabled then the pulse period is reduced by the
  456. * dead time period specified by the user.
  457. *
  458. * param base PWM peripheral base address
  459. * param subModule PWM submodule to configure
  460. * param pwmSignal Signal (PWM A or PWM B) to update
  461. * param currPwmMode The current PWM mode set during PWM setup
  462. * param dutyCycle New PWM pulse width, value should be between 0 to 65535
  463. * 0=inactive signal(0% duty cycle)...
  464. * 65535=active signal (100% duty cycle)
  465. */
  466. void PWM_UpdatePwmDutycycleHighAccuracy(
  467. PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, pwm_mode_t currPwmMode, uint16_t dutyCycle)
  468. {
  469. assert((uint16_t)pwmSignal < 2U);
  470. uint16_t pulseCnt = 0, pwmHighPulse = 0;
  471. uint16_t modulo = 0;
  472. switch (currPwmMode)
  473. {
  474. case kPWM_SignedCenterAligned:
  475. modulo = base->SM[subModule].VAL1 + 1U;
  476. pulseCnt = modulo * 2U;
  477. /* Calculate pulse width */
  478. pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;
  479. /* Setup the PWM dutycycle */
  480. if (pwmSignal == kPWM_PwmA)
  481. {
  482. base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U);
  483. base->SM[subModule].VAL3 = (pwmHighPulse / 2U);
  484. }
  485. else
  486. {
  487. base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U);
  488. base->SM[subModule].VAL5 = (pwmHighPulse / 2U);
  489. }
  490. break;
  491. case kPWM_CenterAligned:
  492. pulseCnt = base->SM[subModule].VAL1 + 1U;
  493. /* Calculate pulse width */
  494. pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;
  495. /* Setup the PWM dutycycle */
  496. if (pwmSignal == kPWM_PwmA)
  497. {
  498. base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U);
  499. base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U);
  500. }
  501. else
  502. {
  503. base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U);
  504. base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U);
  505. }
  506. break;
  507. case kPWM_SignedEdgeAligned:
  508. modulo = base->SM[subModule].VAL1 + 1U;
  509. pulseCnt = modulo * 2U;
  510. /* Calculate pulse width */
  511. pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;
  512. /* Setup the PWM dutycycle */
  513. if (pwmSignal == kPWM_PwmA)
  514. {
  515. base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo);
  516. base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse;
  517. }
  518. else
  519. {
  520. base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo);
  521. base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse;
  522. }
  523. break;
  524. case kPWM_EdgeAligned:
  525. pulseCnt = base->SM[subModule].VAL1 + 1U;
  526. /* Calculate pulse width */
  527. pwmHighPulse = (pulseCnt * dutyCycle) / 65535U;
  528. /* Setup the PWM dutycycle */
  529. if (pwmSignal == kPWM_PwmA)
  530. {
  531. base->SM[subModule].VAL2 = 0;
  532. base->SM[subModule].VAL3 = pwmHighPulse;
  533. }
  534. else
  535. {
  536. base->SM[subModule].VAL4 = 0;
  537. base->SM[subModule].VAL5 = pwmHighPulse;
  538. }
  539. break;
  540. default:
  541. assert(false);
  542. break;
  543. }
  544. }
  545. /*!
  546. * brief Sets up the PWM input capture
  547. *
  548. * Each PWM submodule has 3 pins that can be configured for use as input capture pins. This function
  549. * sets up the capture parameters for each pin and enables the pin for input capture operation.
  550. *
  551. * param base PWM peripheral base address
  552. * param subModule PWM submodule to configure
  553. * param pwmChannel Channel in the submodule to setup
  554. * param inputCaptureParams Parameters passed in to set up the input pin
  555. */
  556. void PWM_SetupInputCapture(PWM_Type *base,
  557. pwm_submodule_t subModule,
  558. pwm_channels_t pwmChannel,
  559. const pwm_input_capture_param_t *inputCaptureParams)
  560. {
  561. uint16_t reg = 0;
  562. switch (pwmChannel)
  563. {
  564. case kPWM_PwmA:
  565. /* Setup the capture paramters for PWM A pin */
  566. reg = (PWM_CAPTCTRLA_INP_SELA(inputCaptureParams->captureInputSel) |
  567. PWM_CAPTCTRLA_EDGA0(inputCaptureParams->edge0) | PWM_CAPTCTRLA_EDGA1(inputCaptureParams->edge1) |
  568. PWM_CAPTCTRLA_ONESHOTA(inputCaptureParams->enableOneShotCapture) |
  569. PWM_CAPTCTRLA_CFAWM(inputCaptureParams->fifoWatermark));
  570. /* Enable the edge counter if using the output edge counter */
  571. if (inputCaptureParams->captureInputSel)
  572. {
  573. reg |= PWM_CAPTCTRLA_EDGCNTA_EN_MASK;
  574. }
  575. /* Enable input capture operation */
  576. reg |= PWM_CAPTCTRLA_ARMA_MASK;
  577. base->SM[subModule].CAPTCTRLA = reg;
  578. /* Setup the compare value when using the edge counter as source */
  579. base->SM[subModule].CAPTCOMPA = PWM_CAPTCOMPA_EDGCMPA(inputCaptureParams->edgeCompareValue);
  580. /* Setup PWM A pin for input capture */
  581. base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMA_EN_SHIFT + (uint16_t)subModule));
  582. break;
  583. case kPWM_PwmB:
  584. /* Setup the capture paramters for PWM B pin */
  585. reg = (PWM_CAPTCTRLB_INP_SELB(inputCaptureParams->captureInputSel) |
  586. PWM_CAPTCTRLB_EDGB0(inputCaptureParams->edge0) | PWM_CAPTCTRLB_EDGB1(inputCaptureParams->edge1) |
  587. PWM_CAPTCTRLB_ONESHOTB(inputCaptureParams->enableOneShotCapture) |
  588. PWM_CAPTCTRLB_CFBWM(inputCaptureParams->fifoWatermark));
  589. /* Enable the edge counter if using the output edge counter */
  590. if (inputCaptureParams->captureInputSel)
  591. {
  592. reg |= PWM_CAPTCTRLB_EDGCNTB_EN_MASK;
  593. }
  594. /* Enable input capture operation */
  595. reg |= PWM_CAPTCTRLB_ARMB_MASK;
  596. base->SM[subModule].CAPTCTRLB = reg;
  597. /* Setup the compare value when using the edge counter as source */
  598. base->SM[subModule].CAPTCOMPB = PWM_CAPTCOMPB_EDGCMPB(inputCaptureParams->edgeCompareValue);
  599. /* Setup PWM B pin for input capture */
  600. base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMB_EN_SHIFT + (uint16_t)subModule));
  601. break;
  602. case kPWM_PwmX:
  603. reg = (PWM_CAPTCTRLX_INP_SELX(inputCaptureParams->captureInputSel) |
  604. PWM_CAPTCTRLX_EDGX0(inputCaptureParams->edge0) | PWM_CAPTCTRLX_EDGX1(inputCaptureParams->edge1) |
  605. PWM_CAPTCTRLX_ONESHOTX(inputCaptureParams->enableOneShotCapture) |
  606. PWM_CAPTCTRLX_CFXWM(inputCaptureParams->fifoWatermark));
  607. /* Enable the edge counter if using the output edge counter */
  608. if (inputCaptureParams->captureInputSel)
  609. {
  610. reg |= PWM_CAPTCTRLX_EDGCNTX_EN_MASK;
  611. }
  612. /* Enable input capture operation */
  613. reg |= PWM_CAPTCTRLX_ARMX_MASK;
  614. base->SM[subModule].CAPTCTRLX = reg;
  615. /* Setup the compare value when using the edge counter as source */
  616. base->SM[subModule].CAPTCOMPX = PWM_CAPTCOMPX_EDGCMPX(inputCaptureParams->edgeCompareValue);
  617. /* Setup PWM X pin for input capture */
  618. base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMX_EN_SHIFT + (uint16_t)subModule));
  619. break;
  620. default:
  621. assert(false);
  622. break;
  623. }
  624. }
  625. /*!
  626. * @brief Sets up the PWM fault input filter.
  627. *
  628. * @param base PWM peripheral base address
  629. * @param faultInputFilterParams Parameters passed in to set up the fault input filter.
  630. */
  631. void PWM_SetupFaultInputFilter(PWM_Type *base, const pwm_fault_input_filter_param_t *faultInputFilterParams)
  632. {
  633. assert(NULL != faultInputFilterParams);
  634. /* When changing values for fault period from a non-zero value, first write a value of 0 to clear the filter. */
  635. if (0U != (base->FFILT & PWM_FFILT_FILT_PER_MASK))
  636. {
  637. base->FFILT &= ~(uint16_t)(PWM_FFILT_FILT_PER_MASK);
  638. }
  639. base->FFILT = (uint16_t)(PWM_FFILT_FILT_PER(faultInputFilterParams->faultFilterPeriod) |
  640. PWM_FFILT_FILT_CNT(faultInputFilterParams->faultFilterCount) |
  641. PWM_FFILT_GSTR(faultInputFilterParams->faultGlitchStretch ? 1U : 0U));
  642. }
  643. /*!
  644. * brief Sets up the PWM fault protection.
  645. *
  646. * PWM has 4 fault inputs.
  647. *
  648. * param base PWM peripheral base address
  649. * param faultNum PWM fault to configure.
  650. * param faultParams Pointer to the PWM fault config structure
  651. */
  652. void PWM_SetupFaults(PWM_Type *base, pwm_fault_input_t faultNum, const pwm_fault_param_t *faultParams)
  653. {
  654. assert(faultParams);
  655. uint16_t reg;
  656. reg = base->FCTRL;
  657. /* Set the faults level-settting */
  658. if (faultParams->faultLevel)
  659. {
  660. reg |= ((uint16_t)1U << (PWM_FCTRL_FLVL_SHIFT + (uint16_t)faultNum));
  661. }
  662. else
  663. {
  664. reg &= ~((uint16_t)1U << (PWM_FCTRL_FLVL_SHIFT + (uint16_t)faultNum));
  665. }
  666. /* Set the fault clearing mode */
  667. if ((uint16_t)faultParams->faultClearingMode != 0U)
  668. {
  669. /* Use manual fault clearing */
  670. reg &= ~((uint16_t)1U << (PWM_FCTRL_FAUTO_SHIFT + (uint16_t)faultNum));
  671. if (faultParams->faultClearingMode == kPWM_ManualSafety)
  672. {
  673. /* Use manual fault clearing with safety mode enabled */
  674. reg |= ((uint16_t)1U << (PWM_FCTRL_FSAFE_SHIFT + (uint16_t)faultNum));
  675. }
  676. else
  677. {
  678. /* Use manual fault clearing with safety mode disabled */
  679. reg &= ~((uint16_t)1U << (PWM_FCTRL_FSAFE_SHIFT + (uint16_t)faultNum));
  680. }
  681. }
  682. else
  683. {
  684. /* Use automatic fault clearing */
  685. reg |= ((uint16_t)1U << (PWM_FCTRL_FAUTO_SHIFT + (uint16_t)faultNum));
  686. }
  687. base->FCTRL = reg;
  688. /* Set the combinational path option */
  689. if (faultParams->enableCombinationalPath)
  690. {
  691. /* Combinational path from the fault input to the PWM output is available */
  692. base->FCTRL2 &= ~((uint16_t)1U << (uint16_t)faultNum);
  693. }
  694. else
  695. {
  696. /* No combinational path available, only fault filter & latch signal can disable PWM output */
  697. base->FCTRL2 |= ((uint16_t)1U << (uint16_t)faultNum);
  698. }
  699. /* Initially clear both recovery modes */
  700. reg = base->FSTS;
  701. reg &= ~(((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum)) |
  702. ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum)));
  703. /* Setup fault recovery */
  704. switch (faultParams->recoverMode)
  705. {
  706. case kPWM_NoRecovery:
  707. break;
  708. case kPWM_RecoverHalfCycle:
  709. reg |= ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum));
  710. break;
  711. case kPWM_RecoverFullCycle:
  712. reg |= ((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum));
  713. break;
  714. case kPWM_RecoverHalfAndFullCycle:
  715. reg |= ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum));
  716. reg |= ((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum));
  717. break;
  718. default:
  719. assert(false);
  720. break;
  721. }
  722. base->FSTS = reg;
  723. }
  724. /*!
  725. * brief Fill in the PWM fault config struct with the default settings
  726. *
  727. * The default values are:
  728. * code
  729. * config->faultClearingMode = kPWM_Automatic;
  730. * config->faultLevel = false;
  731. * config->enableCombinationalPath = true;
  732. * config->recoverMode = kPWM_NoRecovery;
  733. * endcode
  734. * param config Pointer to user's PWM fault config structure.
  735. */
  736. void PWM_FaultDefaultConfig(pwm_fault_param_t *config)
  737. {
  738. assert(config);
  739. /* Initializes the configure structure to zero. */
  740. (void)memset(config, 0, sizeof(*config));
  741. /* PWM uses automatic fault clear mode */
  742. config->faultClearingMode = kPWM_Automatic;
  743. /* PWM fault level is set to logic 0 */
  744. config->faultLevel = false;
  745. /* Combinational Path from fault input is enabled */
  746. config->enableCombinationalPath = true;
  747. /* PWM output will stay inactive when recovering from a fault */
  748. config->recoverMode = kPWM_NoRecovery;
  749. }
  750. /*!
  751. * brief Selects the signal to output on a PWM pin when a FORCE_OUT signal is asserted.
  752. *
  753. * The user specifies which channel to configure by supplying the submodule number and whether
  754. * to modify PWM A or PWM B within that submodule.
  755. *
  756. * param base PWM peripheral base address
  757. * param subModule PWM submodule to configure
  758. * param pwmChannel Channel to configure
  759. * param mode Signal to output when a FORCE_OUT is triggered
  760. */
  761. void PWM_SetupForceSignal(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmChannel, pwm_force_signal_t mode)
  762. {
  763. uint16_t shift;
  764. uint16_t reg;
  765. /* DTSRCSEL register has 4 bits per submodule; 2 bits for PWM A and 2 bits for PWM B */
  766. shift = ((uint16_t)subModule * 4U) + ((uint16_t)pwmChannel * 2U);
  767. /* Setup the signal to be passed upon occurrence of a FORCE_OUT signal */
  768. reg = base->DTSRCSEL;
  769. reg &= ~((uint16_t)0x3U << shift);
  770. reg |= (uint16_t)((uint16_t)mode << shift);
  771. base->DTSRCSEL = reg;
  772. }
  773. /*!
  774. * brief Enables the selected PWM interrupts
  775. *
  776. * param base PWM peripheral base address
  777. * param subModule PWM submodule to configure
  778. * param mask The interrupts to enable. This is a logical OR of members of the
  779. * enumeration ::pwm_interrupt_enable_t
  780. */
  781. void PWM_EnableInterrupts(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask)
  782. {
  783. /* Upper 16 bits are for related to the submodule */
  784. base->SM[subModule].INTEN |= ((uint16_t)mask & 0xFFFFU);
  785. /* Fault related interrupts */
  786. base->FCTRL |= ((uint16_t)(mask >> 16U) & PWM_FCTRL_FIE_MASK);
  787. }
  788. /*!
  789. * brief Disables the selected PWM interrupts
  790. *
  791. * param base PWM peripheral base address
  792. * param subModule PWM submodule to configure
  793. * param mask The interrupts to enable. This is a logical OR of members of the
  794. * enumeration ::pwm_interrupt_enable_t
  795. */
  796. void PWM_DisableInterrupts(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask)
  797. {
  798. base->SM[subModule].INTEN &= ~((uint16_t)mask & 0xFFFFU);
  799. base->FCTRL &= ~((uint16_t)(mask >> 16U) & PWM_FCTRL_FIE_MASK);
  800. }
  801. /*!
  802. * brief Gets the enabled PWM interrupts
  803. *
  804. * param base PWM peripheral base address
  805. * param subModule PWM submodule to configure
  806. *
  807. * return The enabled interrupts. This is the logical OR of members of the
  808. * enumeration ::pwm_interrupt_enable_t
  809. */
  810. uint32_t PWM_GetEnabledInterrupts(PWM_Type *base, pwm_submodule_t subModule)
  811. {
  812. uint32_t enabledInterrupts;
  813. enabledInterrupts = base->SM[subModule].INTEN;
  814. enabledInterrupts |= (((uint32_t)base->FCTRL & PWM_FCTRL_FIE_MASK) << 16UL);
  815. return enabledInterrupts;
  816. }
  817. /*!
  818. * brief Gets the PWM status flags
  819. *
  820. * param base PWM peripheral base address
  821. * param subModule PWM submodule to configure
  822. *
  823. * return The status flags. This is the logical OR of members of the
  824. * enumeration ::pwm_status_flags_t
  825. */
  826. uint32_t PWM_GetStatusFlags(PWM_Type *base, pwm_submodule_t subModule)
  827. {
  828. uint32_t statusFlags;
  829. statusFlags = base->SM[subModule].STS;
  830. statusFlags |= (((uint32_t)base->FSTS & PWM_FSTS_FFLAG_MASK) << 16UL);
  831. return statusFlags;
  832. }
  833. /*!
  834. * brief Clears the PWM status flags
  835. *
  836. * param base PWM peripheral base address
  837. * param subModule PWM submodule to configure
  838. * param mask The status flags to clear. This is a logical OR of members of the
  839. * enumeration ::pwm_status_flags_t
  840. */
  841. void PWM_ClearStatusFlags(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask)
  842. {
  843. uint16_t reg;
  844. base->SM[subModule].STS = ((uint16_t)mask & 0xFFFFU);
  845. reg = base->FSTS;
  846. /* Clear the fault flags and set only the ones we wish to clear as the fault flags are cleared
  847. * by writing a login one
  848. */
  849. reg &= ~(uint16_t)(PWM_FSTS_FFLAG_MASK);
  850. reg |= (uint16_t)((mask >> 16U) & PWM_FSTS_FFLAG_MASK);
  851. base->FSTS = reg;
  852. }