pwmout_api.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /* mbed Microcontroller Library
  2. *******************************************************************************
  3. * Copyright (c) 2015, STMicroelectronics
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice,
  10. * this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. * 3. Neither the name of STMicroelectronics nor the names of its contributors
  15. * may be used to endorse or promote products derived from this software
  16. * without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  22. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *******************************************************************************
  29. */
  30. #include "pwmout_api.h"
  31. #if DEVICE_PWMOUT
  32. #include "cmsis.h"
  33. #include "pinmap.h"
  34. #include "mbed_error.h"
  35. #include "PeripheralPins.h"
  36. #include "pwmout_device.h"
  37. static TIM_HandleTypeDef TimHandle;
  38. void pwmout_init(pwmout_t *obj, PinName pin)
  39. {
  40. // Get the peripheral name from the pin and assign it to the object
  41. obj->pwm = (PWMName)pinmap_peripheral(pin, PinMap_PWM);
  42. MBED_ASSERT(obj->pwm != (PWMName)NC);
  43. // Get the functions (timer channel, (non)inverted) from the pin and assign it to the object
  44. uint32_t function = pinmap_function(pin, PinMap_PWM);
  45. MBED_ASSERT(function != (uint32_t)NC);
  46. obj->channel = STM_PIN_CHANNEL(function);
  47. obj->inverted = STM_PIN_INVERTED(function);
  48. // Enable TIM clock
  49. #if defined(TIM1_BASE)
  50. if (obj->pwm == PWM_1) {
  51. __HAL_RCC_TIM1_CLK_ENABLE();
  52. }
  53. #endif
  54. #if defined(TIM2_BASE)
  55. if (obj->pwm == PWM_2) {
  56. __HAL_RCC_TIM2_CLK_ENABLE();
  57. }
  58. #endif
  59. #if defined(TIM3_BASE)
  60. if (obj->pwm == PWM_3) {
  61. __HAL_RCC_TIM3_CLK_ENABLE();
  62. }
  63. #endif
  64. #if defined(TIM4_BASE)
  65. if (obj->pwm == PWM_4) {
  66. __HAL_RCC_TIM4_CLK_ENABLE();
  67. }
  68. #endif
  69. #if defined(TIM5_BASE)
  70. if (obj->pwm == PWM_5) {
  71. __HAL_RCC_TIM5_CLK_ENABLE();
  72. }
  73. #endif
  74. #if defined(TIM8_BASE)
  75. if (obj->pwm == PWM_8) {
  76. __HAL_RCC_TIM8_CLK_ENABLE();
  77. }
  78. #endif
  79. #if defined(TIM9_BASE)
  80. if (obj->pwm == PWM_9) {
  81. __HAL_RCC_TIM9_CLK_ENABLE();
  82. }
  83. #endif
  84. #if defined(TIM10_BASE)
  85. if (obj->pwm == PWM_10) {
  86. __HAL_RCC_TIM10_CLK_ENABLE();
  87. }
  88. #endif
  89. #if defined(TIM11_BASE)
  90. if (obj->pwm == PWM_11) {
  91. __HAL_RCC_TIM11_CLK_ENABLE();
  92. }
  93. #endif
  94. #if defined(TIM12_BASE)
  95. if (obj->pwm == PWM_12) {
  96. __HAL_RCC_TIM12_CLK_ENABLE();
  97. }
  98. #endif
  99. #if defined(TIM13_BASE)
  100. if (obj->pwm == PWM_13) {
  101. __HAL_RCC_TIM13_CLK_ENABLE();
  102. }
  103. #endif
  104. #if defined(TIM14_BASE)
  105. if (obj->pwm == PWM_14) {
  106. __HAL_RCC_TIM14_CLK_ENABLE();
  107. }
  108. #endif
  109. #if defined(TIM15_BASE)
  110. if (obj->pwm == PWM_15) {
  111. __HAL_RCC_TIM15_CLK_ENABLE();
  112. }
  113. #endif
  114. #if defined(TIM16_BASE)
  115. if (obj->pwm == PWM_16) {
  116. __HAL_RCC_TIM16_CLK_ENABLE();
  117. }
  118. #endif
  119. #if defined(TIM17_BASE)
  120. if (obj->pwm == PWM_17) {
  121. __HAL_RCC_TIM17_CLK_ENABLE();
  122. }
  123. #endif
  124. #if defined(TIM18_BASE)
  125. if (obj->pwm == PWM_18) {
  126. __HAL_RCC_TIM18_CLK_ENABLE();
  127. }
  128. #endif
  129. #if defined(TIM19_BASE)
  130. if (obj->pwm == PWM_19) {
  131. __HAL_RCC_TIM19_CLK_ENABLE();
  132. }
  133. #endif
  134. #if defined(TIM20_BASE)
  135. if (obj->pwm == PWM_20) {
  136. __HAL_RCC_TIM20_CLK_ENABLE();
  137. }
  138. #endif
  139. #if defined(TIM21_BASE)
  140. if (obj->pwm == PWM_21) {
  141. __HAL_RCC_TIM21_CLK_ENABLE();
  142. }
  143. #endif
  144. #if defined(TIM22_BASE)
  145. if (obj->pwm == PWM_22) {
  146. __HAL_RCC_TIM22_CLK_ENABLE();
  147. }
  148. #endif
  149. // Configure GPIO
  150. pinmap_pinout(pin, PinMap_PWM);
  151. obj->pin = pin;
  152. obj->period = 0;
  153. obj->pulse = 0;
  154. obj->prescaler = 1;
  155. pwmout_period_us(obj, 20000); // 20 ms per default
  156. }
  157. void pwmout_free(pwmout_t *obj)
  158. {
  159. // Configure GPIO
  160. pin_function(obj->pin, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
  161. }
  162. void pwmout_write(pwmout_t *obj, float value)
  163. {
  164. TIM_OC_InitTypeDef sConfig;
  165. int channel = 0;
  166. TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);
  167. if (value < (float)0.0) {
  168. value = 0.0;
  169. } else if (value > (float)1.0) {
  170. value = 1.0;
  171. }
  172. obj->pulse = (uint32_t)((float)obj->period * value);
  173. // Configure channels
  174. sConfig.OCMode = TIM_OCMODE_PWM1;
  175. sConfig.Pulse = obj->pulse / obj->prescaler;
  176. sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
  177. sConfig.OCFastMode = TIM_OCFAST_DISABLE;
  178. #if defined(TIM_OCIDLESTATE_RESET)
  179. sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
  180. #endif
  181. #if defined(TIM_OCNIDLESTATE_RESET)
  182. sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  183. sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  184. #endif
  185. switch (obj->channel) {
  186. case 1:
  187. channel = TIM_CHANNEL_1;
  188. break;
  189. case 2:
  190. channel = TIM_CHANNEL_2;
  191. break;
  192. case 3:
  193. channel = TIM_CHANNEL_3;
  194. break;
  195. case 4:
  196. channel = TIM_CHANNEL_4;
  197. break;
  198. default:
  199. return;
  200. }
  201. if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, channel) != HAL_OK) {
  202. error("Cannot initialize PWM\n");
  203. }
  204. #if !defined(PWMOUT_INVERTED_NOT_SUPPORTED)
  205. if (obj->inverted) {
  206. HAL_TIMEx_PWMN_Start(&TimHandle, channel);
  207. } else
  208. #endif
  209. {
  210. HAL_TIM_PWM_Start(&TimHandle, channel);
  211. }
  212. }
  213. float pwmout_read(pwmout_t *obj)
  214. {
  215. float value = 0;
  216. if (obj->period > 0) {
  217. value = (float)(obj->pulse) / (float)(obj->period);
  218. }
  219. return ((value > (float)1.0) ? (float)(1.0) : (value));
  220. }
  221. void pwmout_period(pwmout_t *obj, float seconds)
  222. {
  223. pwmout_period_us(obj, seconds * 1000000.0f);
  224. }
  225. void pwmout_period_ms(pwmout_t *obj, int ms)
  226. {
  227. pwmout_period_us(obj, ms * 1000);
  228. }
  229. void pwmout_period_us(pwmout_t *obj, int us)
  230. {
  231. TimHandle.Instance = (TIM_TypeDef *)(obj->pwm);
  232. RCC_ClkInitTypeDef RCC_ClkInitStruct;
  233. uint32_t PclkFreq = 0;
  234. uint32_t APBxCLKDivider = RCC_HCLK_DIV1;
  235. float dc = pwmout_read(obj);
  236. uint8_t i = 0;
  237. __HAL_TIM_DISABLE(&TimHandle);
  238. // Get clock configuration
  239. // Note: PclkFreq contains here the Latency (not used after)
  240. HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &PclkFreq);
  241. /* Parse the pwm / apb mapping table to find the right entry */
  242. while (pwm_apb_map_table[i].pwm != obj->pwm) {
  243. i++;
  244. }
  245. if (pwm_apb_map_table[i].pwm == 0) {
  246. error("Unknown PWM instance");
  247. }
  248. if (pwm_apb_map_table[i].pwmoutApb == PWMOUT_ON_APB1) {
  249. PclkFreq = HAL_RCC_GetPCLK1Freq();
  250. APBxCLKDivider = RCC_ClkInitStruct.APB1CLKDivider;
  251. } else {
  252. #if !defined(PWMOUT_APB2_NOT_SUPPORTED)
  253. PclkFreq = HAL_RCC_GetPCLK2Freq();
  254. APBxCLKDivider = RCC_ClkInitStruct.APB2CLKDivider;
  255. #endif
  256. }
  257. /* By default use, 1us as SW pre-scaler */
  258. obj->prescaler = 1;
  259. // TIMxCLK = PCLKx when the APB prescaler = 1 else TIMxCLK = 2 * PCLKx
  260. if (APBxCLKDivider == RCC_HCLK_DIV1) {
  261. TimHandle.Init.Prescaler = (((PclkFreq) / 1000000)) - 1; // 1 us tick
  262. } else {
  263. TimHandle.Init.Prescaler = (((PclkFreq * 2) / 1000000)) - 1; // 1 us tick
  264. }
  265. TimHandle.Init.Period = (us - 1);
  266. /* In case period or pre-scalers are out of range, loop-in to get valid values */
  267. while ((TimHandle.Init.Period > 0xFFFF) || (TimHandle.Init.Prescaler > 0xFFFF)) {
  268. obj->prescaler = obj->prescaler * 2;
  269. if (APBxCLKDivider == RCC_HCLK_DIV1) {
  270. TimHandle.Init.Prescaler = (((PclkFreq) / 1000000) * obj->prescaler) - 1;
  271. } else {
  272. TimHandle.Init.Prescaler = (((PclkFreq * 2) / 1000000) * obj->prescaler) - 1;
  273. }
  274. TimHandle.Init.Period = (us - 1) / obj->prescaler;
  275. /* Period decreases and prescaler increases over loops, so check for
  276. * possible out of range cases */
  277. if ((TimHandle.Init.Period < 0xFFFF) && (TimHandle.Init.Prescaler > 0xFFFF)) {
  278. error("Cannot initialize PWM\n");
  279. break;
  280. }
  281. }
  282. TimHandle.Init.ClockDivision = 0;
  283. TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  284. if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK) {
  285. error("Cannot initialize PWM\n");
  286. }
  287. // Save for future use
  288. obj->period = us;
  289. // Set duty cycle again
  290. pwmout_write(obj, dc);
  291. __HAL_TIM_ENABLE(&TimHandle);
  292. }
  293. void pwmout_pulsewidth(pwmout_t *obj, float seconds)
  294. {
  295. pwmout_pulsewidth_us(obj, seconds * 1000000.0f);
  296. }
  297. void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
  298. {
  299. pwmout_pulsewidth_us(obj, ms * 1000);
  300. }
  301. void pwmout_pulsewidth_us(pwmout_t *obj, int us)
  302. {
  303. float value = (float)us / (float)obj->period;
  304. pwmout_write(obj, value);
  305. }
  306. #endif