打印
[牛人杂谈]

MS51通过ADC控制PWM示例详解

[复制链接]
573|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
huahuagg|  楼主 | 2023-6-29 21:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
AD, ADC, PWM, dc, s51
/**************************************************************************//**
* [url=home.php?mod=space&uid=288409]@file[/url] main.c
* [url=home.php?mod=space&uid=895143]@version[/url] V1.00
* [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.
*
* SPDX-License-Identifier: Apache-2.0
* [url=home.php?mod=space&uid=17282]@CopyRight[/url] (C) 2022 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/


#include "MS51_8K.h"

/*---------------------------------------------------------------------------------------------------------*/
/* Define                                                                                                                                 */
/*---------------------------------------------------------------------------------------------------------*/

/* main-loop switch-case state */
#define IDLE_STATE       0
#define ADC_UPDATE_STATE   1

/**
* PWM frequency = FPWM / {(PWM0PH, PWM0PL) + 1}
* 10kHz         = 24MHz / {(PWM0PH, PWM0PL) + 1}
* (PWM0PH, PWM0PL) = 2399(0x95F)
*/
#define PWM_FREQ   0x95F
#define ROUNDING(x) (2x-(int)(x))

/*---------------------------------------------------------------------------------------------------------*/
/* Global variables                                                                                        */
/*---------------------------------------------------------------------------------------------------------*/
bit g_bADC_Flag = 0;
unsigned int xdata u32_ADCdataAIN;
double i64_ADCdataConvert;

/*---------------------------------------------------------------------------------------------------------*/
/* Functions                                                                                                           */
/*---------------------------------------------------------------------------------------------------------*/
/**
* @brief ADC interrupt service routine
*
* @param[in] None
*
* [url=home.php?mod=space&uid=266161]@return[/url] None
*
* [url=home.php?mod=space&uid=1543424]@Details[/url] Get ADC conversion result.
*/
void ADC_ISR(void) interrupt 11
{
    _push_(SFRS);

    clr_ADCCON0_ADCF;//Clear ADC interrupt flag

    u32_ADCdataAIN = ADCRH << 4;
    u32_ADCdataAIN |= ADCRL;
    g_bADC_Flag = 1;
    _pop_(SFRS);
}

/**
* @brief Initialize the PWM module with specified configurations. It could be clock source, PWM channel and frequency.
*        The default duty cycle is 50%.
*
* @param[in] None
*
* @return None
*
* @details None
*/
void PWM0_Init(void)
{
    /* Define PWM0 clock source and divider. */
    PWM0_ClockSource(PWM_FSYS, 1);

    /* Setting PWM0 channel 2 */
    ENABLE_PWM0_CH2_P10_OUTPUT;
    P10_PUSHPULL_MODE;
    PWM0_ConfigOutputChannel(2, Independent, EdgeAligned, PWM_FREQ, 50);

    PWM0_RUN();
}

/**
* @brief Update PWM duty cycle.
*
* @param[in] u16PWM0Duty Set PWM default duty cycle. The range is 0%~100%
*
* @return None
*
* @details The formula is:
*          PWM duty cycle = PWM duty register /  (PWM period register + 1)
*          -> PWM duty register = PWM duty cycle * (PWM period register + 1)
*/
void PWM0_Reload(unsigned int u16PWM0Duty)
{
    /* Reload PWM duty */
    unsigned int u16PWM0DutyCycleTemp = 0;
    u16PWM0DutyCycleTemp = ((PWM_FREQ + 1) / 100) * u16PWM0Duty;

    PWM2H = u16PWM0DutyCycleTemp >> 8;
    PWM2L = u16PWM0DutyCycleTemp;

    set_PWMCON0_LOAD;
}

/**
* @brief Initialize the ADC module with specified configurations and start ADC convert.
*
* @param[in] None
*
* @return None
*
* @details None
*/
void ADC_Convert(void)
{
    /*Enable channel 5 */
    ENABLE_ADC_CH5;
    ADCCON1 |= 0x30;          /* clock divider */
    ADCCON2 |= 0x0E;          /* AQT time */
    AUXR1 |= SET_BIT4;        /* ADC clock low speed */
    clr_ADCCON0_ADCF;
    set_ADCCON0_ADCS;

    /* Enable ADC interrupt */
    set_IE_EADC;
}

void main(void)
{
    unsigned char state = IDLE_STATE;

    /* FSYS = 24MHz */
    MODIFY_HIRC(HIRC_24);
    PWM0_Init();
    ENABLE_GLOBAL_INTERRUPT;

    while (1)
    {
        switch (state)
        {
            case IDLE_STATE:
            default:
                /* Enable channel 5 */
                ADC_Convert();
                state = ADC_UPDATE_STATE;
                break;


            case ADC_UPDATE_STATE:

                if (g_bADC_Flag == 1)
                {
                    g_bADC_Flag = 0;

                    i64_ADCdataConvert = u32_ADCdataAIN;
                    i64_ADCdataConvert = i64_ADCdataConvert * 100 / 4095;
                    /* Integer value */
                    i64_ADCdataConvert = (2 * i64_ADCdataConvert - (int)(i64_ADCdataConvert));

                    PWM0_Reload((unsigned int)i64_ADCdataConvert);
                    DISABLE_ADC;
                    state = IDLE_STATE;
                }

                break;
        }
    }
}



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

定义和全局变量部分:定义了一些常量和全局变量,包括状态定义、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涉及到多个页,否则可能会导致寄存器操作错误

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

139

主题

1254

帖子

2

粉丝