[DemoCode下载] M031通过寄存器操作实现PWM 触发ADC采样

[复制链接]
499|1
 楼主| 小明的同学 发表于 2022-5-14 21:14 | 显示全部楼层 |阅读模式
  1. /**************************************************************************//**
  2. * [url=home.php?mod=space&uid=288409]@file[/url]     main.c
  3. * [url=home.php?mod=space&uid=895143]@version[/url]  V3.00
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]    Demonstrate how to trigger ADC by PWM.
  5. *
  6. * SPDX-License-Identifier: Apache-2.0
  7. * [url=home.php?mod=space&uid=17282]@CopyRight[/url] (C) 2018 Nuvoton Technology Corp. All rights reserved.
  8. ******************************************************************************/
  9. #include <stdio.h>
  10. #include "NuMicro.h"


  11. /*---------------------------------------------------------------------------------------------------------*/
  12. /* Define global variables and constants                                                                   */
  13. /*---------------------------------------------------------------------------------------------------------*/
  14. volatile uint32_t g_u32AdcIntFlag, g_u32COVNUMFlag = 0;


  15. void SYS_Init(void)
  16. {
  17.     /* Unlock protected registers */
  18.     SYS_UnlockReg();

  19.     /* Enable HIRC */
  20.     CLK->PWRCTL |= CLK_PWRCTL_HIRCEN_Msk;

  21.     /* Waiting for HIRC clock ready */
  22.     while((CLK->STATUS & CLK_STATUS_HIRCSTB_Msk) != CLK_STATUS_HIRCSTB_Msk);

  23.     /* Switch HCLK clock source to HIRC */
  24.     CLK->CLKSEL0 = (CLK->CLKSEL0 & ~CLK_CLKSEL0_HCLKSEL_Msk) | CLK_CLKSEL0_HCLKSEL_HIRC;
  25.     CLK->CLKDIV0 = (CLK->CLKDIV0 & ~CLK_CLKDIV0_HCLKDIV_Msk) | CLK_CLKDIV0_HCLK(1);

  26.     /* Set both PCLK0 and PCLK1 as HCLK/2 */
  27.     CLK->PCLKDIV = (CLK_PCLKDIV_APB0DIV_DIV2 | CLK_PCLKDIV_APB1DIV_DIV2);

  28.     /* Switch UART0 clock source to HIRC */
  29.     CLK->CLKSEL1 = (CLK->CLKSEL1 & ~CLK_CLKSEL1_UART0SEL_Msk) | CLK_CLKSEL1_UART0SEL_HIRC;
  30.     CLK->CLKDIV0 = (CLK->CLKDIV0 & ~CLK_CLKDIV0_UART0DIV_Msk) | CLK_CLKDIV0_UART0(1);

  31.     /* ADC clock source is PCLK1, set divider to 1 */
  32.     CLK->CLKSEL2 = (CLK->CLKSEL2 & ~CLK_CLKSEL2_ADCSEL_Msk) | CLK_CLKSEL2_ADCSEL_PCLK1;
  33.     CLK->CLKDIV0 = (CLK->CLKDIV0 & ~CLK_CLKDIV0_ADCDIV_Msk) | CLK_CLKDIV0_ADC(1);

  34.     /* Enable UART0 and ADC peripheral clock */
  35.     CLK->APBCLK0 |= (CLK_APBCLK0_UART0CKEN_Msk | CLK_APBCLK0_ADCCKEN_Msk);

  36.     /* Enable PWM0 module clock */
  37.     CLK->APBCLK1 |= CLK_APBCLK1_PWM0CKEN_Msk;

  38.     /* Select PWM0 module clock source as PCLK0 */
  39.     CLK->CLKSEL2 = (CLK->CLKSEL2 & ~CLK_CLKSEL2_PWM0SEL_Msk) | CLK_CLKSEL2_PWM0SEL_PCLK0;

  40.     /* Update System Core Clock */
  41.     /* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
  42.     SystemCoreClockUpdate();

  43.     /*----------------------------------------------------------------------*/
  44.     /* Init I/O Multi-function                                              */
  45.     /*----------------------------------------------------------------------*/
  46.     /* Set GPB multi-function pins for UART0 RXD and TXD */
  47.     SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk)) |
  48.                     (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);

  49.     /* Set PB.2 ~ PB.3 to input mode */
  50.     PB->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);
  51.     /* Configure the GPB2 - GPB3 ADC analog input pins.  */
  52.     SYS->GPB_MFPL = (SYS->GPB_MFPL & ~(SYS_GPB_MFPL_PB2MFP_Msk | SYS_GPB_MFPL_PB3MFP_Msk)) |
  53.                     (SYS_GPB_MFPL_PB2MFP_ADC0_CH2 | SYS_GPB_MFPL_PB3MFP_ADC0_CH3);

  54.     /* Disable the GPB2 digital input path to avoid the leakage current. */
  55.     PB->DINOFF |= ((BIT2|BIT3)<<GPIO_DINOFF_DINOFF0_Pos);

  56.     /* Set PA multi-function pins for PWM0 Channel 0 */
  57.     SYS->GPA_MFPL = (SYS->GPA_MFPL & (~SYS_GPA_MFPL_PA0MFP_Msk)) |
  58.                     (SYS_GPA_MFPL_PA5MFP_PWM0_CH0);

  59.     /* Lock protected registers */
  60.     SYS_LockReg();
  61. }

  62. /*----------------------------------------------------------------------*/
  63. /* Init UART0                                                           */
  64. /*----------------------------------------------------------------------*/
  65. void UART0_Init(void)
  66. {
  67.     /* Reset UART0 */
  68.     SYS->IPRST1 |=  SYS_IPRST1_UART0RST_Msk;
  69.     SYS->IPRST1 &= ~SYS_IPRST1_UART0RST_Msk;

  70.     /* Configure UART0 and set UART0 baud rate */
  71.     UART0->BAUD = UART_BAUD_MODE2 | UART_BAUD_MODE2_DIVIDER(__HIRC, 115200);
  72.     UART0->LINE = UART_WORD_LEN_8 | UART_PARITY_NONE | UART_STOP_BIT_1;
  73. }

  74. void PWM0_Init()
  75. {
  76.     /* Set PWM0 timer clock prescaler */
  77.     *(__IO uint32_t *) (&(PWM0->CLKPSC[0])) = 0;

  78.     /* Set up counter type */
  79.     PWM0->CTL1 &= ~PWM_CTL1_CNTTYPE0_Msk;

  80.     /* Set PWM0 timer duty */
  81.     PWM0->CMPDAT[0] = 1000;

  82.     /* Set PWM0 timer period */
  83.     PWM0->PERIOD[0] = 2000;

  84.     /* PWM period point trigger ADC enable */
  85.     PWM0->ADCTS0 = (PWM0->ADCTS0 & ~(PWM_ADCTS0_TRGSEL0_Msk)) |
  86.                    (PWM_ADCTS0_TRGEN0_Msk | PWM_TRIGGER_ADC_EVEN_PERIOD_POINT);

  87.     /* Set output level at zero, compare up, period(center) and compare down of specified channel */
  88.     {
  89.         int i;
  90.         for(i = 0; i < 6; i++)
  91.         {
  92.             if((BIT0) & (1 << i))
  93.             {
  94.                 PWM0->WGCTL0 = ((PWM0->WGCTL0 & ~(3UL << (i << 1))) | (PWM_OUTPUT_HIGH << (i << 1)));
  95.                 PWM0->WGCTL0 = ((PWM0->WGCTL0 & ~(3UL << (PWM_WGCTL0_PRDPCTL0_Pos + (i << 1)))) | (PWM_OUTPUT_NOTHING << (PWM_WGCTL0_PRDPCTL0_Pos + (i << 1))));
  96.                 PWM0->WGCTL1 = ((PWM0->WGCTL1 & ~(3UL << (i << 1))) | (PWM_OUTPUT_LOW << (i << 1)));
  97.                 PWM0->WGCTL1 = ((PWM0->WGCTL1 & ~(3UL << (PWM_WGCTL1_CMPDCTL0_Pos + (i << 1)))) | (PWM_OUTPUT_NOTHING << (PWM_WGCTL1_CMPDCTL0_Pos + (i << 1))));
  98.             }
  99.         }
  100.     }

  101.     /* Enable output of PWM0 channel 0 */
  102.     PWM0->POEN |= BIT0;
  103. }

  104. void ADC_FunctionTest()
  105. {
  106.     uint8_t  u8Option;
  107.     int32_t  i32ConversionData[6] = {0};

  108.     printf("\n");
  109.     printf("+----------------------------------------------------------------------+\n");
  110.     printf("|                      ADC trigger by PWM test                         |\n");
  111.     printf("+----------------------------------------------------------------------+\n");

  112.     printf("\nIn this test, software will get 6 conversion result from the specified channel.\n");

  113.     /* Enable ADC converter */
  114.     ADC->ADCR |= ADC_ADCR_ADEN_Msk;

  115.     /* Do calibration for ADC to decrease the effect of electrical random noise. */
  116.     ADC->ADCALSTSR |= ADC_ADCALSTSR_CALIF_Msk;  /* Clear Calibration Finish Interrupt Flag */
  117.     ADC->ADCALR |= ADC_ADCALR_CALEN_Msk;        /* Enable Calibration function */
  118.     ADC_START_CONV(ADC);                        /* Start to calibration */
  119.     while((ADC->ADCALSTSR & ADC_ADCALSTSR_CALIF_Msk) != ADC_ADCALSTSR_CALIF_Msk);   /* Wait calibration finish */

  120.     while(1)
  121.     {
  122.         printf("Select input mode:\n");
  123.         printf("  [1] Single end input (channel 2 only)\n");
  124.         printf("  [2] Differential input (channel pair 1 only)\n");
  125.         printf("  Other keys: exit single mode test\n");
  126.         u8Option = getchar();
  127.         if(u8Option == '1')
  128.         {
  129.             /* Set input mode as single-end, Single mode, and select channel 2 */
  130.             /* Configure the sample module and enable PWM0 trigger source */
  131.             ADC->ADCR = (ADC->ADCR & ~(ADC_ADCR_DIFFEN_Msk | ADC_ADCR_ADMD_Msk | ADC_ADCR_TRGS_Msk | ADC_ADCR_TRGCOND_Msk | ADC_ADCR_TRGEN_Msk)) |
  132.                         (ADC_ADCR_DIFFEN_SINGLE_END | ADC_ADCR_ADMD_SINGLE | ADC_ADCR_TRGS_PWM | ADC_ADCR_TRGEN_Msk | ADC_ADCR_ADIE_Msk);
  133.             ADC->ADCHER = (ADC->ADCHER & ~ADC_ADCHER_CHEN_Msk) | (BIT2);

  134.             /* Clear the A/D interrupt flag for safe */
  135.             ADC->ADSR0 = ADC_ADF_INT;

  136.             /* Enable the sample module interrupt */
  137.             NVIC_EnableIRQ(ADC_IRQn);

  138.             printf("Conversion result of channel 2:\n");

  139.             /* Reset the ADC indicator and enable PWM0 channel 0 counter */
  140.             g_u32AdcIntFlag = 0;
  141.             g_u32COVNUMFlag = 0;
  142.             PWM0->CNTEN |= PWM_CH_0_MASK;   /* PWM0 channel 0 counter start running. */

  143.             while(1)
  144.             {
  145.                 /* Wait ADC interrupt (g_u32AdcIntFlag will be set at IRQ_Handler function) */
  146.                 while(g_u32AdcIntFlag == 0);

  147.                 /* Reset the ADC interrupt indicator */
  148.                 g_u32AdcIntFlag = 0;

  149.                 /* Get the conversion result of the ADC channel 2 */
  150.                 i32ConversionData[g_u32COVNUMFlag - 1] = (ADC->ADDR[2] & ADC_ADDR_RSLT_Msk);

  151.                 if(g_u32COVNUMFlag >= 6)
  152.                     break;
  153.             }

  154.             /* Disable PWM0 channel 0 counter */
  155.             PWM0->CNTEN &= ~BIT0;   /* PWM0 counter stop running. */

  156.             for(g_u32COVNUMFlag = 0; (g_u32COVNUMFlag) < 6; g_u32COVNUMFlag++)
  157.                 printf("                                0x%X (%d)\n", i32ConversionData[g_u32COVNUMFlag], i32ConversionData[g_u32COVNUMFlag]);
  158.         }
  159.         else if(u8Option == '2')
  160.         {
  161.             /* Set input mode as differential, Single mode, and select channel 2 */
  162.             /* Configure the sample module and enable PWM0 trigger source */
  163.             ADC->ADCR = (ADC->ADCR & ~(ADC_ADCR_DIFFEN_Msk | ADC_ADCR_ADMD_Msk | ADC_ADCR_TRGS_Msk | ADC_ADCR_TRGCOND_Msk | ADC_ADCR_TRGEN_Msk)) |
  164.                         (ADC_ADCR_DIFFEN_DIFFERENTIAL | ADC_ADCR_ADMD_SINGLE | ADC_ADCR_TRGS_PWM | ADC_ADCR_TRGEN_Msk | ADC_ADCR_ADIE_Msk);
  165.             ADC->ADCHER = (ADC->ADCHER & ~ADC_ADCHER_CHEN_Msk) | (BIT2);

  166.             /* Clear the A/D interrupt flag for safe */
  167.             ADC->ADSR0 = ADC_ADF_INT;

  168.             /* Enable the sample module interrupt */
  169.             NVIC_EnableIRQ(ADC_IRQn);

  170.             printf("Conversion result of channel 2:\n");

  171.             /* Reset the ADC indicator and enable PWM0 channel 0 counter */
  172.             g_u32AdcIntFlag = 0;
  173.             g_u32COVNUMFlag = 0;
  174.             PWM0->CNTEN |= PWM_CH_0_MASK;   /* PWM0 channel 0 counter start running. */

  175.             while(1)
  176.             {
  177.                 /* Wait ADC interrupt (g_u32AdcIntFlag will be set at IRQ_Handler function) */
  178.                 while(g_u32AdcIntFlag == 0);

  179.                 /* Reset the ADC interrupt indicator */
  180.                 g_u32AdcIntFlag = 0;

  181.                 /* Get the conversion result of the ADC channel 2 */
  182.                 i32ConversionData[g_u32COVNUMFlag - 1] = (ADC->ADDR[2] & ADC_ADDR_RSLT_Msk);

  183.                 if(g_u32COVNUMFlag >= 6)
  184.                     break;
  185.             }

  186.             /* Disable PWM0 channel 0 counter */
  187.             PWM0->CNTEN &= ~BIT0;   /* PWM0 counter stop running. */

  188.             for(g_u32COVNUMFlag = 0; (g_u32COVNUMFlag) < 6; g_u32COVNUMFlag++)
  189.                 printf("                                0x%X (%d)\n", i32ConversionData[g_u32COVNUMFlag], i32ConversionData[g_u32COVNUMFlag]);
  190.         }
  191.         else
  192.             return ;
  193.     }
  194. }

  195. void ADC_IRQHandler(void)
  196. {
  197.     ADC->ADSR0 = ADC_ADF_INT;   /* Clear the A/D interrupt flag */
  198.     g_u32AdcIntFlag = 1;
  199.     g_u32COVNUMFlag++;
  200. }

  201. int32_t main(void)
  202. {
  203.     /* Init System, IP clock and multi-function I/O. */
  204.     SYS_Init();

  205.     /* Init UART0 for printf */
  206.     UART0_Init();

  207.     /* Init PWM for ADC */
  208.     PWM0_Init();

  209.     printf("\nSystem clock rate: %d Hz", SystemCoreClock);

  210.     /* ADC function test */
  211.     ADC_FunctionTest();

  212.     /* Disable ADC IP clock */
  213.     CLK->APBCLK0 &= ~(CLK_APBCLK0_ADCCKEN_Msk);

  214.     /* Disable PWM0 IP clock */
  215.     CLK->APBCLK1 &= ~(CLK_APBCLK1_PWM0CKEN_Msk);

  216.     /* Disable External Interrupt */
  217.     NVIC_DisableIRQ(ADC_IRQn);

  218.     printf("Exit ADC sample code\n");

  219.     while(1);
  220. }


 楼主| 小明的同学 发表于 2022-5-14 21:14 | 显示全部楼层
如果对ADC和PWM寄存器了解的人,肯定喜欢这种操作了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

159

主题

1640

帖子

2

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