[牛人杂谈] MS51通过ADC控制PWM示例详解

[复制链接]
1084|3
 楼主| huahuagg 发表于 2023-6-29 21:25 | 显示全部楼层 |阅读模式
AD, ADC, PWM, dc, s51
  1. /**************************************************************************//**
  2. * [url=home.php?mod=space&uid=288409]@file[/url] main.c
  3. * [url=home.php?mod=space&uid=895143]@version[/url] V1.00
  4. * [url=home.php?mod=space&uid=247401]@brief[/url] This example code uses MS51 PWM to output 10 KHz waveform with 0~100% duty according to 0~3V ADC conversion.
  5. *
  6. * SPDX-License-Identifier: Apache-2.0
  7. * [url=home.php?mod=space&uid=17282]@CopyRight[/url] (C) 2022 Nuvoton Technology Corp. All rights reserved.
  8. *****************************************************************************/


  9. #include "MS51_8K.h"

  10. /*---------------------------------------------------------------------------------------------------------*/
  11. /* Define                                                                                                                                 */
  12. /*---------------------------------------------------------------------------------------------------------*/

  13. /* main-loop switch-case state */
  14. #define IDLE_STATE       0
  15. #define ADC_UPDATE_STATE   1

  16. /**
  17. * PWM frequency = FPWM / {(PWM0PH, PWM0PL) + 1}
  18. * 10kHz         = 24MHz / {(PWM0PH, PWM0PL) + 1}
  19. * (PWM0PH, PWM0PL) = 2399(0x95F)
  20. */
  21. #define PWM_FREQ   0x95F
  22. #define ROUNDING(x) (2x-(int)(x))

  23. /*---------------------------------------------------------------------------------------------------------*/
  24. /* Global variables                                                                                        */
  25. /*---------------------------------------------------------------------------------------------------------*/
  26. bit g_bADC_Flag = 0;
  27. unsigned int xdata u32_ADCdataAIN;
  28. double i64_ADCdataConvert;

  29. /*---------------------------------------------------------------------------------------------------------*/
  30. /* Functions                                                                                                           */
  31. /*---------------------------------------------------------------------------------------------------------*/
  32. /**
  33. * @brief ADC interrupt service routine
  34. *
  35. * @param[in] None
  36. *
  37. * [url=home.php?mod=space&uid=266161]@return[/url] None
  38. *
  39. * [url=home.php?mod=space&uid=1543424]@Details[/url] Get ADC conversion result.
  40. */
  41. void ADC_ISR(void) interrupt 11
  42. {
  43.     _push_(SFRS);

  44.     clr_ADCCON0_ADCF;//Clear ADC interrupt flag

  45.     u32_ADCdataAIN = ADCRH << 4;
  46.     u32_ADCdataAIN |= ADCRL;
  47.     g_bADC_Flag = 1;
  48.     _pop_(SFRS);
  49. }

  50. /**
  51. * @brief Initialize the PWM module with specified configurations. It could be clock source, PWM channel and frequency.
  52. *        The default duty cycle is 50%.
  53. *
  54. * @param[in] None
  55. *
  56. * @return None
  57. *
  58. * @details None
  59. */
  60. void PWM0_Init(void)
  61. {
  62.     /* Define PWM0 clock source and divider. */
  63.     PWM0_ClockSource(PWM_FSYS, 1);

  64.     /* Setting PWM0 channel 2 */
  65.     ENABLE_PWM0_CH2_P10_OUTPUT;
  66.     P10_PUSHPULL_MODE;
  67.     PWM0_ConfigOutputChannel(2, Independent, EdgeAligned, PWM_FREQ, 50);

  68.     PWM0_RUN();
  69. }

  70. /**
  71. * @brief Update PWM duty cycle.
  72. *
  73. * @param[in] u16PWM0Duty Set PWM default duty cycle. The range is 0%~100%
  74. *
  75. * @return None
  76. *
  77. * @details The formula is:
  78. *          PWM duty cycle = PWM duty register /  (PWM period register + 1)
  79. *          -> PWM duty register = PWM duty cycle * (PWM period register + 1)
  80. */
  81. void PWM0_Reload(unsigned int u16PWM0Duty)
  82. {
  83.     /* Reload PWM duty */
  84.     unsigned int u16PWM0DutyCycleTemp = 0;
  85.     u16PWM0DutyCycleTemp = ((PWM_FREQ + 1) / 100) * u16PWM0Duty;

  86.     PWM2H = u16PWM0DutyCycleTemp >> 8;
  87.     PWM2L = u16PWM0DutyCycleTemp;

  88.     set_PWMCON0_LOAD;
  89. }

  90. /**
  91. * @brief Initialize the ADC module with specified configurations and start ADC convert.
  92. *
  93. * @param[in] None
  94. *
  95. * @return None
  96. *
  97. * @details None
  98. */
  99. void ADC_Convert(void)
  100. {
  101.     /*Enable channel 5 */
  102.     ENABLE_ADC_CH5;
  103.     ADCCON1 |= 0x30;          /* clock divider */
  104.     ADCCON2 |= 0x0E;          /* AQT time */
  105.     AUXR1 |= SET_BIT4;        /* ADC clock low speed */
  106.     clr_ADCCON0_ADCF;
  107.     set_ADCCON0_ADCS;

  108.     /* Enable ADC interrupt */
  109.     set_IE_EADC;
  110. }

  111. void main(void)
  112. {
  113.     unsigned char state = IDLE_STATE;

  114.     /* FSYS = 24MHz */
  115.     MODIFY_HIRC(HIRC_24);
  116.     PWM0_Init();
  117.     ENABLE_GLOBAL_INTERRUPT;

  118.     while (1)
  119.     {
  120.         switch (state)
  121.         {
  122.             case IDLE_STATE:
  123.             default:
  124.                 /* Enable channel 5 */
  125.                 ADC_Convert();
  126.                 state = ADC_UPDATE_STATE;
  127.                 break;


  128.             case ADC_UPDATE_STATE:

  129.                 if (g_bADC_Flag == 1)
  130.                 {
  131.                     g_bADC_Flag = 0;

  132.                     i64_ADCdataConvert = u32_ADCdataAIN;
  133.                     i64_ADCdataConvert = i64_ADCdataConvert * 100 / 4095;
  134.                     /* Integer value */
  135.                     i64_ADCdataConvert = (2 * i64_ADCdataConvert - (int)(i64_ADCdataConvert));

  136.                     PWM0_Reload((unsigned int)i64_ADCdataConvert);
  137.                     DISABLE_ADC;
  138.                     state = IDLE_STATE;
  139.                 }

  140.                 break;
  141.         }
  142.     }
  143. }



代码主要包含以下几个部分:

定义和全局变量部分:定义了一些常量和全局变量,包括状态定义、PWM频率、ADC数据等。

ADC_ISR函数:这是ADC的中断服务函数,当ADC转换完成时会触发该中断,将转换结果存储在全局变量中。

PWM0_Init函数:用于初始化PWM模块,设置PWM时钟源、频率和输出通道。

PWM0_Reload函数:根据给定的占空比参数,重新加载PWM的占空比。

ADC_Convert函数:初始化ADC模块,并启动ADC转换。

main函数:主函数中包含一个状态机,通过状态机控制程序的执行流程。在IDLE_STATE下启动ADC转换,并切换到ADC_UPDATE_STATE状态。在ADC_UPDATE_STATE下,当ADC转换完成时,计算并转换ADC数据为占空比,然后重新加载PWM的占空比,并切换回IDLE_STATE状态。

总体来说,这段代码的作用是通过ADC输入控制PWM输出的占空比,实现根据模拟输入电压调节PWM波形的功能。

 楼主| huahuagg 发表于 2023-6-29 21:28 | 显示全部楼层
_push_(SFRS); 和 _pop_(SFRS); 是两个用于保存和恢复寄存器状态的宏指令。这些宏指令用于将特定寄存器组(Special Function Register Set,SFRS)的状态保存到堆栈,并在需要时从堆栈中恢复。

在嵌入式系统中,特定寄存器组通常包含关键的系统配置和状态信息。在中断服务函数中,为了避免中断处理过程中对这些寄存器造成干扰,常常需要保存和恢复寄存器的状态。

_push_(SFRS); 操作会将当前的SFRS状态压入堆栈,保存起来。这样,在执行一段中断服务代码之前,可以确保寄存器状态的一致性。

_pop_(SFRS); 操作会从堆栈中弹出之前保存的SFRS状态,并将其恢复到寄存器中。这样,中断服务代码执行完毕后,可以恢复之前的寄存器状态,确保不影响正常的程序执行。

综合起来,_push_(SFRS); 和 _pop_(SFRS); 这两个操作可以保护和恢复关键寄存器的状态,确保中断服务代码的执行不会干扰到正常的程序流程。
598330983 发表于 2023-6-30 21:30 | 显示全部楼层
你不说状态机我还真没看出来。另外这个中断的压栈出栈操作是什么时候需要,我看很多单片机没这个操作啊。
598330983 发表于 2023-6-30 21:31 | 显示全部楼层
查了一下资料,原来是因为有多个寄存器页导致的。
因为MS51的SRF涉及到多个页,否则可能会导致寄存器操作错误
您需要登录后才可以回帖 登录 | 注册

本版积分规则

160

主题

1437

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部