楼主: 奋斗哥

STM32F4从入门到深入,很好的教程系列转过来一起学习!

  [复制链接]
  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 10:44:57 | 显示全部楼层
    分享到:
    本帖最后由 奋斗哥 于 2013-7-26 13:48 编辑

    通用定时器


          通用定时器(TIM2 to TIM5)包括由可编程的分频器驱动一个的16位或32位自动重载计数器。它们可用于多种用途,包括测量输入信号的脉冲长度(输入捕获)或生成的输出波形(输出比较和PWM)。可以使用定时器的预分频器和RCC时钟控制器分频器调制从几微秒到几毫秒的脉冲长度和波形周期。它们是完全独立的,不共享任何资源。

    主要特点

    通用TIMx定时器功能包括:

    1.  16位(TIM3和TIM4)或32位(TIM2和TIM5)计数器,向上,向下,向上/向下自动重装计数。

    2.  16位可编程预分频器(可在运行时改变)用于在1到65535之间细分计数器的时钟频率。

    3.  多达4个独立的通道可用于:

    - 输入捕捉

    - 输出比较

    - PWM生成(边缘和中心对齐模式)

    - 单脉冲输出模式

    4. 使用外部信号控制定时器和定时器互连的同步电路

    5. 对以下事件中断/ DMA的生成:

    - 更新:计数器溢出/下溢,计数器初始化(通过软件或内部/外部触发)

    - 触发事件(计数器开始,停止,初始化或内部/外部触发计数)

    - 输入捕捉

    - 输出比较

    6. 支持针对定位增量(正交)编码器和霍尔传感器电路

    7. 触发输入作为外部时钟或者按周期的电流管理

    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 10:45:22 | 显示全部楼层
    本帖最后由 奋斗哥 于 2013-7-26 13:49 编辑

    PWM

             脉宽调制模式可以生成一个由TIMx_ARR寄存器的值确定频率和TIMx_CCRx寄存器的值确定占空比的信号。

             可以对每个通道独立选择PWM模式,( OCx输出一个PWM信号)在TIMx_CCMRx寄存器的OCxM位写110(PWM模式1)或111(PWM模式2)。必须通过设置TIMx_CCMRx寄存器的OCxPE位,启用相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。

           仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR寄存器中的UG位来初始化所有的寄存器。

            OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能。详见TIMx_CCERx寄存器的描述。

    配置步骤:

    1.     使能相关时钟。

    2.     配置相关的引脚模式、速度、以及复用功能。

    3.     使能TIM时钟

    4.     设置分频。

    5.     设置周期。(ARR的值)周期=(PSC+1)* ARR / TIMx时钟

    6.     产生一次更新事件,更新影子寄存器的值。

    7.     在CCMR中设置PWM模式。

    8.     设置各通道占空比。占空比= CCRx / ARR。

    9.     使能比较输出。

    10.  启动预装载。

    11.  打开定时器。



    程序:

    1. /************************************
    2.     标题:输出4路PWM
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M
    6.    
    7.     author:小船
    8.     data:2012-02-08
    9. *************************************/

    10. #include <stm32f4xx.h>

    11. void main ()
    12. {   

    13.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1
    14.   
    15.   RCC->AHB1ENR |= (1<<2); //打开GPIOC时钟
    16.   GPIOC->MODER |= 0X000AA000;//pc6789第二功能
    17.         //推挽输出
    18.   GPIOC->OSPEEDR |= 0x000ff000;//速度100m
    19.   GPIOC->PUPDR |= 0x00055000;//上拉  
    20.   
    21.   GPIOC->AFR[0] |= 0x22000000;//pc6789第二功能AF2
    22.   GPIOC->AFR[1] |= 0x00000022;
    23.   
    24.   RCC->APB1ENR |= (1<<1); //打开TIM3时钟
    25.   TIM3->PSC = 83; //对时钟84M进行84分频,使得计数频率为1M
    26.   TIM3->ARR = 10000;  //周期10ms
    27.   TIM3->EGR |= 1; //产生一次更新事件
    28.   
    29.   TIM3->CCMR1 |= 0x6060;//PWM模式1
    30.   TIM3->CCMR2 |= 0x6060;//PWM模式1
    31.   
    32.   TIM3->CCR1 = 8000;//设置第一通道占空比80%
    33.   TIM3->CCR2 = 6000;//设置第二通道占空比60%
    34.   TIM3->CCR3 = 4000; //设置第三通道占空比40%
    35.   TIM3->CCR4 = 2000; //设置第四通道占空比20%
    36.   
    37.   TIM3->CCER |= 0x1111;//使能比较输出
    38.   
    39.   TIM3->CCMR1 |= 0x0808;//启动预装载
    40.   TIM3->CCMR2 |= 0x0808;
    41.   
    42.   TIM3->CR1 |= 1; //开始计时

    43.   while(1)
    44.   {
    45.   };
    46. }
    复制代码
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 10:46:02 | 显示全部楼层
    本帖最后由 奋斗哥 于 2013-7-26 13:55 编辑

    DAC

           Stm32f4内嵌的DAC是12位数字输入,电压输出的数模转换器。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+以获得更精确的转换结果。

            

    主要特点:

    2个DAC转换器:每个转换器对应1个输出通道

    8位或者12位单调输出

    12位模式下数据左对齐或者右对齐

    同步更新功能

    噪声波形生成

    三角波形生成

    双DAC通道同时或者分别转换

    每个通道都有DMA功能

    外部触发转换

    输入参考电压VREF+


    设置步骤:

    1.     设置相关GPIO(PA4、5)。

    2.     若时钟源为TIMx更新事件,则设置TIMx。

    3.     使能DAC时钟

    4.     设置触发源,开启相关触发通道,如果用DMA,是否允许数据错误中断。

    5.     如果用DMA。配置相关DMA数据流。

    程序:

    1. /************************************
    2.     标题:DAC
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M
    6.    
    7.     author:小船
    8.     data:2012-02-12
    9. *************************************/

    10. #include <stm32f4xx.h>
    11. #include "MyDebugger.h"
    12. #include "sintable.h"


    13. void main ()
    14. {   

    15.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

    16.   /***GPIO设置***/
    17.   RCC->AHB1ENR |= (1<<0); //打开GPIOA时钟
    18.   GPIOA->MODER |= 0x00000F00;//PA4、5模拟模式
    19.   GPIOA->PUPDR &= 0xfffff0ff;//无上拉无下拉   
    20.   
    21.   /***定时器设置***/
    22.   RCC->APB1ENR |= (1<<4);//打开TIM6时钟
    23.   TIM6->PSC = 0;
    24.   TIM6->ARR = 83;  //使得更新事件频率为1m
    25.   TIM6->CR2 |=  0x00000020;//更新事件输出
    26.   TIM6->CR1 |= 1; //开始计时
    27.   
    28.   /***DAC设置***/
    29.   RCC->APB1ENR |= (1<<29); //使能DAC时钟
    30.   DAC->CR &= 0xffff0000;
    31.   /*
    32.   使能DMA堵塞中断
    33.   使能通道1触发
    34.   */
    35.   DAC->CR |= ( (1<<13) | (1<<2) );  
    36.   NVIC->IP[54] = 0xA0;
    37.   NVIC->ISER[1] |= (1<<(54-32));

    38.   /***DMA设置***/
    39.   RCC->AHB1ENR |= (1<<21); //使能DMA1时钟
    40.   DAC->CR &= ~(1<<12);//DAC dma发送模式除能
    41.   DMA1_Stream5->CR &= 0xFFFFFFFE; //除能DMA1_Stream5
    42.   while(DMA1_Stream5->CR & 0x00000001);//确保DMA可以被设置
    43.   DMA1->HIFCR |= 0x000004f0;//传送前清空DMA1_Stream5所有中断标志
    44.   DMA1_Stream5->PAR = (uint32_t)&DAC->DHR12R1;//设置外设地址
    45.   DMA1_Stream5->M0AR = (uint32_t)SinTable; //设置内存地址
    46.   DMA1_Stream5->CR |= 0x0002800;//16位数据
    47.   DMA1_Stream5->NDTR = 1024; //设置dma传输数据的数量
    48.   /*
    49.     设置dma通道7,即DAC1
    50.     优先级Medium
    51.     传输方向内存到外设
    52.     内存递增模式
    53.     循环模式
    54.   */
    55.   DMA1_Stream5->CR |= ( 0x0e000000 | 0x00010000 | (1<<6)
    56.                         | (1<<10) | (1<<8) );
    57.   
    58.   DMA1_Stream5->CR |= 1; //DMA数据流5使能
    59.   
    60.   DAC->CR |= (1<<0);   //DAC通道1使能

    61.   DAC->CR |= (1<<12);//DAC dma发送模式使能  
    62.    
    63.   MyDebugger_Init();

    64.   while(1)
    65.   {
    66.   };
    67. }

    68. void TIM6_DAC_IRQHandler(void)
    69. {
    70.   if( DAC->SR & (1<<13) )
    71.   {
    72.     MyDebugger_LEDs(red, on);
    73.     DAC->SR &= ~(1<<13);
    74.   }
    75. }
    复制代码

    ADC

    12位ADC是一种逐次逼近型模拟数字转换器。它有多达19个通道,可测量16个外部和2个内部信号源和Vbat通道。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

    模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。

    有16个多路通道。

    通道选择:

          可以把转换组织成两组:规则组和注入组。在任意多个通道上以任意顺序进行的一系列转换构成成组转换。例如,可以如下顺序完成转换:通道3、通道8、通道2、通道2、通道0、通道2、通道2、通道15。

          规则组由多达16个转换组成。规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。

           注入组由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中。如果ADC_SQRx或ADC_JSQR寄存器在转换期间被更改,当前的转换被清除,一个新的启动脉冲将发送到ADC以转换新选择的组。

    单次转换模式:

            单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。

    一旦选择通道的转换完成:

    如果一个规则通道被转换:

    ─ 转换数据被储存在16位ADC_DR寄存器中

    ─ EOC(转换结束)标志被设置 ─ 如果设置了EOCIE,则产生中断。

    如果一个注入通道被转换:

    ─ 转换数据被储存在16位的ADC_DRJ1寄存器中

    ─ JEOC(注入转换结束)标志被设置 ─ 如果设置了JEOCIE位,则产生中断。

    然后ADC停止

    连续转换模式:

            在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。

    每个转换后:

    如果一个规则通道被转换:

    ─ 转换数据被储存在16位的ADC_DR寄存器中

    ─ EOC(转换结束)标志被设置

    ─ 如果设置了EOCIE,则产生中断。

    注入通道不能被用于连续转换模式,唯一的例外是当规则通道配置为连续转换后,注入通道配置为自动转换。

    扫描模式:

             此模式用来扫描一组模拟通道。

             扫描模式可通过设置ADC_CR1寄存器的SCAN位来选择。一旦这个位被设置,ADC扫描所有被ADC_SQRX寄存器(对规则通道)或ADC_JSQR(对注入通道)选中的所有通道。在每个组的每个通道上执行单次转换。在每个转换结束时,同一组的下一个通道被自动转换。如果设置了CONT位,转换不会在选择组的最后一个通道上停止,而是再次从选择组的第一个通道继续转换。

    如果设置了DMA位,在每次EOC后,DMA控制器把规则组通道的转换数据传输到SRAM中。而注入通道转换的数据总是存储在ADC_JDRx寄存器中。


    在以下情况中,ADC_SR寄存器的EOC位将被设置:
    在每个规则组序列结束后,如果EOCS位被清0
    在每个规则通道转换结束,如果EOCS位设置为1
    注入通道转换结束数据总是存放在ADC_JDRx寄存器中

    注入通道管理:

    触发注入

    清除ADC_CR1寄存器的JAUTO位,并且设置SCAN位,即可使用触发注入功能。

    1. 利用外部触发或通过设置ADC_CR2寄存器的ADON位,启动一组规则通道的转换。

    2. 如果在规则通道转换期间产生一外部注入触发或者JSWSTART位被设置,当前转换被复位,注入通道序列被以单次扫描方式进行转换。

    3. 然后,恢复上次被中断的规则组通道转换。如果在注入转换期间产生一规则事件,注入转换不会被中断,但是规则序列将在注入序列结束后被执行。

    注:当使用触发的注入转换时,必须保证触发事件的间隔长于注入序列。例如:序列长度为30ADC时钟周期(2个具有3个时钟间隔采样时间的转换),触发之间最小的间隔必须是31ADC时钟周期。

    自动注入

    如果设置了JAUTO位,在规则组通道之后,注入组通道被自动转换。这可以用来转换在ADC_SQRx和ADC_JSQR寄存器中设置的多至20个转换序列。

    在此模式里,必须禁止注入通道的外部触发。

    如果除JAUTO位外还设置了CONT位,规则通道至注入通道的转换序列被连续执行。


    间断模式:

    规则组

    此模式通过设置ADC_CR1寄存器上的DISCEN位激活。它可以用来执行一个短序列的n次转换(n<=8),此转换是ADC_SQRx寄存器所选择的转换序列的一部分。数值n由ADC_CR1寄存器的DISCNUM[2:0]位给出。

    一个外部触发信号可以启动ADC_SQRx寄存器中描述的下一轮n次转换,直到此序列所有的转换完成为止。总的序列长度由ADC_SQR1寄存器的L[3:0]定义。

    例如:

    n=3,被转换的通道 = 0、1、2、3、6、7、9、10

    第一次触发:转换的序列为 0、1、2

    第二次触发:转换的序列为 3、6、7

    第三次触发:转换的序列为 9、10,并产生EOC事件

    第四次触发:转换的序列 0、1、2

    注: 当以间断模式转换一个规则组时,转换序列结束后不自动从头开始。 当所有子组被转换完成,下一次触发启动第一个子组的转换。在上面的例子中,第四次触发重新转换第一子组的通道012


    注入组

    此模式通过设置ADC_CR1寄存器的JDISCEN位激活。在一个外部触发事件后,该模式按通道顺序逐个转换ADC_JSQR寄存器中选择的序列。

    一个外部触发信号可以启动ADC_JSQR寄存器选择的下一个通道序列的转换,直到序列中所有的转换完成为止。总的序列长度由ADC_JSQR寄存器的JL[1:0]位定义。

    例如: n=1,被转换的通道 = 1、2、3

    第一次触发:通道1被转换

    第二次触发:通道2被转换

    第三次触发:通道3被转换,并且产生EOC和JEOC事件

    第四次触发:通道1被转换



    设置步骤:

    1. 配置相关输入通道的IO口。

    2. 设置DMA

    3. 如果双重ADC或三重采样,设置ADC的公共寄存器

    4. 配置要使用到的ADC


    程序:
    1. /************************************
    2.     标题:一个ADC连续采样
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M
    6.    
    7.     author:小船
    8.     data:2012-02-14
    9. *************************************/

    10. #include <stm32f4xx.h>
    11. #include "MyDebugger.h"

    12. __IO uint16_t ADC3ConvertedVault[10000];
    13. char TXbuffer[] = "PC1输入电压为:x.xxxV\n\r";

    14. void ADC3_IN11_Config(void);

    15. void main ()
    16. {   
    17.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

    18.   ADC3_IN11_Config();
    19.    
    20.   MyDebugger_Init();

    21.   while(1)
    22.   {
    23.   };
    24. }

    25. void ADC3_IN11_Config(void)
    26. {
    27.     /***GPIO设置***/
    28.   RCC->AHB1ENR |= (1<<2); //打开GPIOC时钟
    29.   GPIOC->MODER &= 0xfffffff3;//PC1模拟模式
    30.   GPIOC->MODER |= 0x0000000C;
    31.   GPIOC->PUPDR &= 0xfffffff3;//无上拉无下拉   

    32.   /***DMA设置***/
    33.   RCC->AHB1ENR |= (1<<22); //使能DMA2时钟
    34.   ADC3->CR2 &= ~(1<<8);//ADC3 dma发送模式除能
    35.   DMA2_Stream0->CR &= 0xFFFFFFFE; //除能DMA2_Stream0
    36.   while(DMA2_Stream0->CR & 0x00000001);//确保DMA可以被设置
    37.   DMA2->LIFCR |= 0x0000003D;//传送前清空DMA1_Stream5所有中断标志
    38.   DMA2_Stream0->PAR = (uint32_t)&ADC3->DR;//设置外设地址
    39.   DMA2_Stream0->M0AR = (uint32_t)ADC3ConvertedVault; //设置内存地址
    40.   DMA2_Stream0->CR |= 0x0002800;//16位数据
    41.   DMA2_Stream0->NDTR = 10000; //设置dma传输数据的数量
    42.   /*
    43.     设置dma2通道2,即ADC3
    44.     优先级Medium
    45.     传输方向外设到内存
    46.     内存递增模式
    47.     循环模式
    48.     传输完成中断
    49.   */
    50.   DMA2_Stream0->CR |= ( 0x04000000 | 0x00010000 | 0x0
    51.                         | (1<<10) | (1<<8) | (1<<4) );
    52.   
    53.   NVIC->IP[56] = 0xB0;
    54.   NVIC->ISER[1] |= (1<<(56-32));
    55.   
    56.   DMA2_Stream0->CR |= 1; //DMA2数据流0使能
    57.   
    58.   /***ADC3设置***/
    59.   RCC->APB2ENR |= (1<<10); //使能ADC3时钟
    60.   ADC3->SQR1 = 0x00000000;//转换一个通道
    61.   ADC3->SQR3 = 0x0000000B;//第一个通道为ADC3_in11
    62.   ADC3->CR1 &= 0x00000000;
    63.   ADC3->CR2 &= 0x00000000;  
    64.   ADC3->CR2 |= (1<<1);  //连续转换
    65.   ADC3->CR2 |= (1<<9); //最后一次ADC转换后发出dma请求
    66.   ADC3->CR2 |= (1<<0);   //开启AD转换
    67.   
    68.   ADC3->CR2 |= (1<<8);//ADC dma发送模式使能
    69.   
    70.   ADC3->CR2 |= (1<<30); //规则通道转换开始
    71. }

    72. void DMA2_Stream0_IRQHandler (void)
    73. {
    74.   uint32_t i;
    75.   uint32_t Average;
    76.   if(DMA2->LISR & 0x00000010)
    77.   {
    78.       DMA2->LIFCR |= 0x00000010;
    79.       for(i = 0; i < 10000; i++)  // 对一万个数据取平均值
    80.         Average += ADC3ConvertedVault[i];
    81.       Average *= 3;
    82.       Average /= 40960;
    83.       TXbuffer[14] = ( Average / 1000 ) % 10 + 0x30;//转换成ASCII码
    84.       TXbuffer[16] = ( Average / 100 ) % 10 + 0x30;
    85.       TXbuffer[17] = ( Average / 10 ) % 10 + 0x30;
    86.       TXbuffer[18] = Average % 10 + 0x30;
    87.       MyDebugger_Message(TXbuffer, sizeof(TXbuffer)/sizeof(char));  
    88.   }
    89. }

    复制代码
    运行结果:
    1347070131_9649.png


    三ADC交替采样

    在多ADC模式中,ADC1为主,ADC2或ADC3为从,交替或者同时触发,工作模式取决于ADC_CCR寄存器的MULTI[4:0]。

           多ADC模式中,转换后的数据可以多模式的数据寄存器(ADC_CDR)中读取。状态可以在多模式的状态寄存器(ADC_CSR)读取。

            

    多ADC模式下的DMA传输方式:

        方式1:每个AD转换完都发出DMA请求,多模式的数据寄存器(ADC_CDR)用低位保存转换结果。

    1st request: ADC_CDR[31:0] = ADC1_DR[15:0]

    2nd request: ADC_CDR[31:0] = ADC2_DR[15:0]

    3rd request: ADC_CDR[31:0] = ADC3_DR[15:0]

    4th request: ADC_CDR[31:0] = ADC1_DR[15:0]

    方式2:每两个AD转换完都发出DMA请求。

    双ADC模式:

    高十六位保存ADC2结果,低十六位保存ADC1结果

    1st request: ADC_CDR[31:0] = ADC2_DR[15:0] |ADC1_DR[15:0]

    2nd request: ADC_CDR[31:0] = ADC2_DR[15:0] |ADC1_DR[15:0]

    三ADC模式:

    1st request: ADC_CDR[31:0] = ADC2_DR[15:0] |ADC1_DR[15:0]

    2nd request: ADC_CDR[31:0] = ADC1_DR[15:0] |ADC3_DR[15:0]

    3rd request: ADC_CDR[31:0] = ADC3_DR[15:0] |ADC2_DR[15:0]

    4th request: ADC_CDR[31:0] = ADC2_DR[15:0] |ADC1_DR[15:0]

    方式2:每两个AD转换完都发出DMA请求。与方式2相似,但是DMA以半字方式传输。

    用于6位或者8位分辨率中。

    双ADC模式:

    高8位保存ADC2结果,低8位保存ADC1结果

    1st request: ADC_CDR[15:0] = ADC2_DR[7:0] | ADC1_DR[7:0]

    2nd request: ADC_CDR[15:0] = ADC2_DR[7:0] | ADC1_DR[7:0]

    三ADC模式:

    1st request: ADC_CDR[15:0] = ADC2_DR[7:0] | ADC1_DR[7:0]

    2nd request: ADC_CDR[15:0] = ADC1_DR[7:0] | ADC3_DR[7:0]

    3rd request: ADC_CDR[15:0] = ADC3_DR[7:0] | ADC2_DR[7:0]

    4th request: ADC_CDR[15:0] = ADC2_DR[7:0] | ADC1_DR[7:0]

    多ADC转换模式:

    1.     注入同步模式

    2.     规则同步模式

    3.     交替模式

    4.     交替触发模式

    5.     规则同步+注入同步模式

    6.     规则同步+交替触发模式

    交替模式配置步骤:

    1. 配置相关输入通道的IO口。

    2. 设置DMA

    3. 如果双重ADC或三重采样,设置ADC的公共寄存器

           a.     设置公共寄存器首先要打开任意一个ADC的时钟,否则这部分数字电路是没有开始工作的。

            b.     设置DMA模式

            c.     设置ADC转换完成后,发送DMA请求

            d.     设置多ADC模式

            e.     设置两次采样间隔周期

    4. 配置要使用到的ADC(必须ADC1为主,其他为从)。

    程序:

    1. /************************************
    2.     标题:ADC
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M
    6.    
    7.     author:小船
    8.     data:2012-02-16
    9. *************************************/

    10. #include <stm32f4xx.h>
    11. #include "MyDebugger.h"

    12. __IO uint16_t ADCConvertedVault[10000];
    13. char TXbuffer[] = "PC1输入电压为:x.xxxV\n\r";

    14. void ADC_IO_Config(void);
    15. void ADC_DMA_Config(void);

    16. void ADC_Common_Config(void);

    17. void ADC3_IN11_Config(void);
    18. void ADC2_IN11_Config(void);
    19. void ADC1_IN11_Config(void);

    20. void main ()
    21. {   
    22.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

    23.   ADC_IO_Config();
    24.   ADC_DMA_Config();
    25.   
    26.   ADC_Common_Config();
    27.   
    28.   ADC3_IN11_Config();
    29.   ADC2_IN11_Config();
    30.   ADC1_IN11_Config();
    31.   
    32.   ADC3->CR2 |= (1<<0);   //开启ADC3转换
    33.   ADC2->CR2 |= (1<<0);   //开启ADC2转换
    34.   ADC1->CR2 |= (1<<0);   //开启ADC1转换
    35.    
    36.   ADC1->CR2 |= (1<<30); //触发转换开始
    37.   
    38.   MyDebugger_Init();

    39.   while(1)
    40.   {
    41.   };
    42. }

    43. void ADC_Common_Config(void)
    44. {
    45.   RCC->APB2ENR |= ( (1<<8) | (1<<9) | (1<<10) ); //使能ADC时钟
    46.   ADC->CCR &= 0x00000000;
    47.   
    48.   /*
    49.   DMA模式1
    50.   最后一次ADC转换后发出dma请求
    51.   交错模式
    52.   2次采样之间的延迟5个周期
    53.   */
    54.   ADC->CCR |= ( 0x00004000 | (1<<13) | 0x00000017 | 0x00000000);
    55.    
    56. }

    57. /***ADC1设置***/
    58. void ADC1_IN11_Config(void)
    59. {   
    60.   ADC1->SQR1 = 0x00000000;//转换一个通道
    61.   ADC1->SQR3 = 0x0000000B;//第一个通道为ADC1_in11
    62.   ADC1->CR1 &= 0x00000000;
    63.   ADC1->CR2 &= 0x00000000;  
    64.   ADC1->CR2 |= (1<<1);  //连续转换
    65.   ADC1->CR2 |= (1<<9); //最后一次ADC转换后发出dma请求

    66.   ADC1->CR2 |= (1<<8);//ADC dma发送模式使能
    67. }

    68. /***ADC2设置***/
    69. void ADC2_IN11_Config(void)
    70. {   
    71.   ADC2->SQR1 = 0x00000000;//转换一个通道
    72.   ADC2->SQR3 = 0x0000000B;//第一个通道为ADC1_in11
    73.   ADC2->CR1 &= 0x00000000;
    74.   ADC2->CR2 &= 0x00000000;  
    75.   ADC2->CR2 |= (1<<1);  //连续转换
    76.   ADC2->CR2 |= (1<<9); //最后一次ADC转换后发出dma请求
    77. }

    78. /***ADC3设置***/
    79. void ADC3_IN11_Config(void)
    80. {   
    81.   ADC3->SQR1 = 0x00000000;//转换一个通道
    82.   ADC3->SQR3 = 0x0000000B;//第一个通道为ADC3_in11
    83.   ADC3->CR1 &= 0x00000000;
    84.   ADC3->CR2 &= 0x00000000;  
    85.   ADC3->CR2 |= (1<<1);  //连续转换
    86.   ADC3->CR2 |= (1<<9); //最后一次ADC转换后发出dma请求
    87. }

    88. /***GPIO设置***/
    89. void ADC_IO_Config(void)
    90. {
    91.   RCC->AHB1ENR |= (1<<2); //打开GPIOC时钟
    92.   GPIOC->MODER &= 0xfffffff3;//PC1模拟模式
    93.   GPIOC->MODER |= 0x0000000C;
    94.   GPIOC->PUPDR &= 0xfffffff3;//无上拉无下拉  
    95. }

    96. /***DMA设置***/
    97. void ADC_DMA_Config(void)
    98. {   
    99.   RCC->AHB1ENR |= (1<<22); //使能DMA2时钟
    100.   ADC3->CR2 &= ~(1<<8);//ADC3 dma发送模式除能
    101.   DMA2_Stream0->CR &= 0xFFFFFFFE; //除能DMA2_Stream0
    102.   while(DMA2_Stream0->CR & 0x00000001);//确保DMA可以被设置
    103.   DMA2->LIFCR |= 0x0000003D;//传送前清空DMA2_Stream0所有中断标志
    104.   DMA2_Stream0->PAR = (uint32_t)&ADC->CDR;//设置外设地址
    105.   DMA2_Stream0->M0AR = (uint32_t)ADCConvertedVault; //设置内存地址
    106.   DMA2_Stream0->CR |= 0x0002800;//16位数据
    107.   DMA2_Stream0->NDTR = 10000; //设置dma传输数据的数量
    108.   /*
    109.     设置dma2通道0,即ADC1
    110.     优先级Medium
    111.     传输方向外设到内存
    112.     内存递增模式
    113.     循环模式
    114.     传输完成中断
    115.   */
    116.   DMA2_Stream0->CR |= ( 0x00000000 | 0x00010000 | 0x0 | (1<<10) | (1<<8) | (1<<4) );
    117.   
    118.   NVIC->IP[56] = 0xB0;
    119.   NVIC->ISER[1] |= (1<<(56-32));
    120.   
    121.   DMA2_Stream0->CR |= 1; //DMA2数据流0使能
    122. }

    123. void DMA2_Stream0_IRQHandler (void)
    124. {
    125.   uint32_t i;
    126.   uint32_t Average;
    127.   if(DMA2->LISR & 0x00000010)
    128.   {
    129.       DMA2->LIFCR |= 0x00000010;
    130.       for(i = 0; i < 10000; i++)  // 对一万个数据取平均值
    131.         Average += ADCConvertedVault[i];
    132.       Average *= 3;
    133.       Average /= 40960;
    134.       TXbuffer[14] = ( Average / 1000 ) % 10 + 0x30;//转换成ASCII码
    135.       TXbuffer[16] = ( Average / 100 ) % 10 + 0x30;
    136.       TXbuffer[17] = ( Average / 10 ) % 10 + 0x30;
    137.       TXbuffer[18] = Average % 10 + 0x30;
    138.       MyDebugger_Message(TXbuffer, sizeof(TXbuffer)/sizeof(char));  
    139.   }
    140. }

    复制代码
    运行结果:
    1347070661_3049.png
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2014-7-16 09:10
  • 签到天数: 361 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2013-7-26 10:46:03 | 显示全部楼层
    {:soso_e127:}真是苦了奋斗哥了。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 10:46:23 | 显示全部楼层
    本帖最后由 奋斗哥 于 2013-7-26 14:02 编辑

    数字滤波


          为了对stm32f4的ADC和DAC有更多的了解,我决定做一个实用性比较强的实验。就是数字滤波实验,利用stm32f4的DAC可以产生噪声的特点,利用它的一路DAC产生叠加噪声的信号作为原始信号。然后用ADC测量,把结果经过滤波处理后用DAC的另一通道把结果输出,用双踪示波器观察。

    一阶惯性滤波器及其数字化

    一阶惯性滤波器的传递函数为:


    利用一阶差分法离散化,可以得到一阶惯性数字滤波算法:


    其中T为采样周期,为滤波时间常数。T和必须根据信号频谱来选择。

    编程实现:

    a.     设定一个1024点正弦波表,用DAC1叠加噪声输出

    b.     配置定时器6更新频率为1M

    c.     DAC的时钟为TIM6更新事件,1024点,频率大概为0.5M

    d.     在TIM6的更新中断中启动一次AD转换

    e.     AD转换中断中做滤波处理,然后把数值送DAC2,启动一次DAC2

    程序:

    1. /************************************
    2.     标题:数字滤波实验
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M

    6.     描述:用DAC1产生一路叠加了噪声的信号
    7.           用ADC通道11测量上面产生的信号
    8.           对ADC的测量结果进行滤波处理
    9.           用DAC2把滤波后的结果输出
    10.    
    11.     author:小船
    12.     data:2012-02-17
    13. *************************************/

    14. #include <stm32f4xx.h>
    15. #include "MyDebugger.h"
    16. #include "sintable.h"

    17. /*********变量声明********/
    18. uint16_t Y0, Y1;//滤波器输出值
    19. float T = 0.000001;//采样周期
    20. float C = 0.00003; //滤波常数

    21. /*********函数声明********/
    22. void timer6_Init(void);
    23. void ADC3_IN11_Config(void);
    24. void DAC_channel2_Config(void);
    25. void Generate_SinSignal_with_Noise(void);

    26. void main ()
    27. {   

    28.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

    29.   MyDebugger_Init();

    30.   ADC3_IN11_Config();
    31.   Generate_SinSignal_with_Noise();
    32.   DAC_channel2_Config();  
    33.   timer6_Init();

    34.   while(1)
    35.   {
    36.   };
    37. }

    38. /**************************************
    39.   函数名:timer6_Init
    40.   参数:无
    41.   返回值:无
    42.   功能:设置定时器6更新频率为1M
    43.         定时器6更新事件为DAC1、2时钟
    44.         更新中断启动ADC检测
    45. **************************************/
    46. void timer6_Init(void)
    47. {
    48.   /***定时器设置***/
    49.   RCC->APB1ENR |= (1<<4);//打开TIM6时钟
    50.   TIM6->PSC = 0;
    51.   TIM6->ARR = 83;  //使得更新事件频率为1m
    52.   TIM6->CR2 |=  0x00000020;//更新事件输出
    53.   TIM6->DIER |= 1; //使能中断
    54.   TIM6->CR1 |= 1; //开始计时
    55. }

    56. /**************************************
    57.   函数名:Generate_SinSignal_with_Noise
    58.   参数:无
    59.   返回值:无
    60.   功能:用DAC1产生一路叠加了噪声的信号
    61. ***************************************/
    62. void Generate_SinSignal_with_Noise(void)
    63. {
    64.   /***GPIO设置***/
    65.   RCC->AHB1ENR |= (1<<0); //打开GPIOA时钟
    66.   GPIOA->MODER |= 0x00000F00;//PA4、5模拟模式
    67.   GPIOA->PUPDR &= 0xfffff0ff;//无上拉无下拉   
    68.   
    69.   /***DAC设置***/
    70.   RCC->APB1ENR |= (1<<29); //使能DAC时钟
    71.   DAC->CR &= 0xffff0000;
    72.   /*
    73.   使能DMA堵塞中断
    74.   使能通道1触发
    75.   叠加噪声
    76.   */
    77.   DAC->CR |= ( (1<<13) | (1<<2) | 0x00000040 | 0x00000800);  
    78.   NVIC->IP[54] = 0xA0;
    79.   NVIC->ISER[1] |= (1<<(54-32));

    80.   /***DMA设置***/
    81.   RCC->AHB1ENR |= (1<<21); //使能DMA1时钟
    82.   DAC->CR &= ~(1<<12);//DAC dma发送模式除能
    83.   DMA1_Stream5->CR &= 0xFFFFFFFE; //除能DMA1_Stream5
    84.   while(DMA1_Stream5->CR & 0x00000001);//确保DMA可以被设置
    85.   DMA1->HIFCR |= 0x000004f0;//传送前清空DMA1_Stream5所有中断标志
    86.   DMA1_Stream5->PAR = (uint32_t)&DAC->DHR12R1;//设置外设地址
    87.   DMA1_Stream5->M0AR = (uint32_t)SinTable; //设置内存地址
    88.   DMA1_Stream5->CR |= 0x0002800;//16位数据
    89.   DMA1_Stream5->NDTR = 1024; //设置dma传输数据的数量
    90.   /*
    91.     设置dma通道7,即DAC1
    92.     优先级Medium
    93.     传输方向内存到外设
    94.     内存递增模式
    95.     循环模式
    96.   */
    97.   DMA1_Stream5->CR |= ( 0x0e000000 | 0x00010000 | (1<<6)
    98.                         | (1<<10) | (1<<8) );
    99.   
    100.   DMA1_Stream5->CR |= 1; //DMA数据流5使能
    101.   
    102.   DAC->CR |= (1<<0);   //DAC通道1使能

    103.   DAC->CR |= (1<<12);//DAC dma发送模式使能
    104. }

    105. /**************************************
    106.   函数名:ADC3_IN11_Config
    107.   参数:无
    108.   返回值:无
    109.   功能:用ADC通道11测量上面产生的信号
    110. ***************************************/
    111. void ADC3_IN11_Config(void)
    112. {
    113.     /***GPIO设置***/
    114.   RCC->AHB1ENR |= (1<<2); //打开GPIOC时钟
    115.   GPIOC->MODER &= 0xfffffff3;//PC1模拟模式
    116.   GPIOC->MODER |= 0x0000000C;
    117.   GPIOC->PUPDR &= 0xfffffff3;//无上拉无下拉   
    118.   
    119.   /***ADC3设置***/
    120.   RCC->APB2ENR |= (1<<10); //使能ADC3时钟
    121.   ADC3->SQR1 = 0x00000000;//转换一个通道
    122.   ADC3->SQR3 = 0x0000000B;//第一个通道为ADC3_in11
    123.   ADC3->CR1 &= 0x00000000;
    124.   ADC3->CR2 &= 0x00000000;  
    125. //单次转换
    126.   ADC3->CR1 |= (1<<5);//使能转换完成中断
    127.   NVIC->IP[18] = 0xc0;
    128.   NVIC->ISER[0] |= (1<<18);
    129. }

    130. /**************************************
    131.   函数名:DAC_channel2_Config
    132.   参数:无
    133.   返回值:无
    134.   功能:用DAC2把滤波后的结果输出
    135. ***************************************/
    136. void DAC_channel2_Config(void)
    137. {
    138.   /***DAC设置***/
    139.   RCC->APB1ENR |= (1<<29); //使能DAC时钟
    140.   DAC->CR &= 0x0000ffff;
    141.   /*
    142.   使能通道2触发
    143.   配置为软件触发
    144.   */
    145.   DAC->CR |= ( (1<<18) | (0x00380000) );
    146.   
    147.   DAC->CR |= (1<<16);   //DAC通道2使能
    148. }

    149. void TIM6_DAC_IRQHandler(void)
    150. {
    151.   if( DAC->SR & (1<<13) )
    152.   {
    153.     MyDebugger_LEDs(red, on);//亮红灯指示DAC1的DMA传输数据错误
    154.     DAC->SR &= ~(1<<13);
    155.   }
    156.   
    157.   if(TIM6->SR)
    158.   {   
    159.     ADC3->CR2 |= (1<<0);   //开启AD转换  
    160.     ADC3->CR2 |= (1<<30); //规则通道转换开始
    161.    
    162.     TIM6->SR &= ~(0x0001);
    163.   }
    164. }

    165. void ADC_IRQHandler(void)
    166. {
    167.   if( ADC3->SR & (1<<1))
    168.   {
    169.     Y0 = (uint16_t)( (float)(( T / C ) * ADC3->DR) //滤波公式
    170.                       + (float)(( 1 - T / C ) * Y1) );
    171.     Y1 = Y0;
    172.     DAC->DHR12R2 = Y0;  //DAC2输出滤波后的结果
    173.     DAC->SWTRIGR |= (1<<1);
    174.    
    175.     ADC3->SR &= ~(1<<1);
    176.   }
    177. }

    复制代码
    输出的信号:
    1347071119_3580.jpg
    用AD测量后不经过滤波直接输出:
    1347071194_9021.jpg
    经过滤波的输出:
    1347071291_9955.jpg

    结论:经过滤波后,很好地把噪声滤除了,但相位有一定的滞后,幅值会变小。


    CAN控制器


    首先简单介绍一下CAN总线,关于CAN总线是谁发明的,CAN总线的历史,CAN总线的发展,CAN总线的应用场合,这些,通通不说。这里只是以我个人理解,简单说说CAN通信。CAN总线的端点没有地址(除非自己在帧里定义地址),CAN总线通信不用地址,用标识符,不是单独的谁发给谁,而是,你总是发送给整个网络。然后每个节点都有过滤器,对网络上的传播的帧的标识符进行过滤,自己想要什么样的帧,可以设置自己的过滤器,接收相关的帧信息。如果两个节点同时发送怎么办?这个不用我们担心,CAN控制器会自己仲裁,让高优先级的帧先发。

             然后我们可以了解一下stm32的CAN控制器。

    1347071680_5834.png

             如上图所示,stm32有两个can控制器,can1(主),和can2(从),其中过滤器的设置是通过can1来设置,其他工作模式,波特率等,可以各自设置。每个控制器有三个发送邮箱,两个fifo,每个fifo有三个接收邮箱。



             发送:选择一个空的发送邮箱,把帧信息写到该发送邮箱的寄存器里,请求发送,控制器就会根据标识符的优先级把帧先后发送出去。

             接收:如果接收到的帧的标识符能过过滤表的一系列过滤,该帧信息就会保存在fifo接收邮箱的寄存器里。

             过滤器:stm32f407共有28组过滤器,每组过滤器可以设置关联到fifo0或者fifo1,每组都包括两个32位存储器,可以配置成一个32位有位屏蔽功能的标识符过滤器,或者两个32位完全匹配的标识符过滤器,或者两个16位有位屏蔽功能的标识符过滤器,或者四个16位完全匹配的标识符过滤器。如下图所示:

    1347071768_9584.png


    我所说的完全匹配的意思是,接收到的帧的标识符每一位都要跟过滤器对应的位一样,才能过得了这个过滤器。有位屏蔽功能的意思是一个寄存器放标识符,一个放屏蔽掩码,屏蔽掩码为1的位对应的接收到的帧的标识符的位与对应的放标识符的寄存器的位一致,就能通过。

    传输一位的时间和波特率的计算:

    1347071818_1800.png

             CAN控制器的波特率是由APB时钟线和CAN位时序寄存器CAN_BTR的TS2[3:0]、TS1[2:0]和BRP[9:0]确定的,其中,TS1[2:0]定义了时间段1占用多少个时间单元,TS2[3:0]定义了时间段2占用多少个时间单元,BRP[9:0]定义对APB1时钟的分频。

    PS:设置波特率为1M

    1347071881_4388.png

    其中Tpclk为APB1的时钟周期,假设为

    Tpclk = 1/42M

    0≦TS1≦7

    0≦TS2≦15

    0≦BRP≦1021

    根据以上数据,有

    (TS2+TS1+3)(BRP+1)=42

    令BRP=2,有

    TS2+TS1=11

    令TS1=8,TS2=3



    设置步骤:

    1.     设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

    2.     使能相关GPIO时钟。

    3.     选择相关GPIO引脚的复用功能。

    4.     设置相关GPIO引脚为复用模式。

    5.     设置相关GPIO引脚的速度,方式。

    6.     设置主控制寄存器MCR,进入初始化模式

    7.     等待进入初始化模式

    8.     设置波特率。

    9.     其他设置。

    10.  如果要用到中断,在中断使能寄存器IER中使能相关中断响应。

    11.  如果要用到中断,设置相关中断优先级(NVIC_IP)。

    12.  如果要用到中断,使能相关中断(NVIC_ISER)。

    13.  设置主控制寄存器MCR,进入正常工作模式。

    14.  设置FMR,使过滤器组工作在初始化模式。

    15.  设置FMR的CAN2SB,确定CAN2的过滤器组从哪一组开始。

    16.  设置用到的过滤器组的工作方式。

    17.  设置用到的过滤器组的位宽。

    18.  给fifo0和fifo2划分(关联)过滤组。

    19.  禁用用到的过滤器组。

    20.  设置过滤器组的标识符,帧类型等。

    21.  使能相关过滤器组。

    22.  设置FMR,使过滤器组工作在正常模式。

    23.  如果要用中断,编写中断服务函数(函数名是固定的)。

    24.  中断服务函数里检查是哪个中断。

    25.  编写相应服务程序。


    电路请参见:小工具之——CAN收发器

    有时候调试程序免不了需要一些小电路,以下电路用两个sn65hvd230做了两个CAN总线收发器在同一块板上。


    JP1,JP4:两个跳线帽,用于配置终端电阻

    JP2,JP3:用于配置波形上升沿斜率


    PCB尺寸:33mm*25mm

    1347159925_4485.png


    1347160492_3204.png





    程序:

    1. /************************************
    2.     标题:操作CAN的练习
    3.     软件平台:IAR for ARM6.21
    4.     硬件平台:stm32f4-discovery
    5.     主频:168M
    6.    
    7.     描述:通过硬件收发器连接CAN1,CAN2
    8.           组成一个两个端点的网络

    9.           CAN1循环发出数据帧

    10.           CAN2接收过滤数据帧

    11.           用uart把CAN2接收到
    12.           的数据帧发到超级终端

    13.     author:小船
    14.     data:2012-08-14
    15. *************************************/

    16. #include <stm32f4xx.h>
    17. #include "MyDebugger.h"

    18. #define RECEIVE_BUFFER_SIZE 20

    19. u32 CAN2_receive_buffer[RECEIVE_BUFFER_SIZE][4];
    20. u8 UART_send_buffer[1800];
    21. u8 Consumer = 0;
    22. u8 Producer = 0;

    23. u32 Gb_TimingDelay;
    24. void Delay(uint32_t nTime);
    25. void TIM7_init();//定时1s
    26. u32 get_rece_data();
    27. void CAN_GPIO_config();

    28. void main ()
    29. {   

    30.   u32 empty_box;
    31.   SysTick_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断
    32.   SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

    33.   MyDebugger_Init();
    34.   TIM7_init();
    35.   MyDebugger_Message( "\n\rtesting......\n\r" ,
    36.                      sizeof("\n\rtesting......\n\r")/sizeof(char) );
    37.   
    38.   CAN_GPIO_config();
    39.   
    40.   RCC->APB1ENR |= ((1<<25)|(1<<26));//使能CAN1、CAN2时钟
    41.   
    42.   CAN1->MCR = 0x00000000;
    43.   /*
    44.   请求进入初始化模式
    45.   禁止报文自动重传
    46.   自动唤醒模式
    47.   */
    48.   CAN1->MCR |= ((1<<0)|(1<<4)|(1<<5));
    49.   CAN1->MCR &= ~(1<<16);//在调试时,CAN照常工作
    50.   
    51.   while(!(CAN1->MSR & 0xfffffffe))  //等待进入初始化模式
    52.   {
    53.     MyDebugger_LEDs(orange, on);
    54.   }
    55.   MyDebugger_LEDs(orange, off);
    56.   
    57.   /*
    58.   正常模式
    59.   重新同步跳跃宽度(1+1)tq
    60.   TS2[2:0]=3
    61.   TS1[3:0]=8
    62.   BRP[9:0]=2
    63.   
    64.   ps:
    65.   tq = (BRP[9:0] + 1) x tPCLK,
    66.   tBS2 = tq x (TS2[2:0] + 1),
    67.   tBS1 = tq x (TS1[3:0] + 1),
    68.   NominalBitTime = 1 × tq+tBS1+tBS2,
    69.   BaudRate = 1 / NominalBitTime
    70.   
    71.   波特率设为1M
    72.   */
    73.   CAN1->BTR = ((0<<30)|(0x01<<24)|(3<<20)|(8<<16)|(2<<0));
    74.   
    75.   CAN1->MCR &= ~(0x00000001);//正常工作模式
    76.   
    77.   CAN2->MCR = 0x00000000;
    78.   /*
    79.   请求进入初始化模式
    80.   禁止报文自动重传
    81.   自动唤醒模式
    82.   */
    83.   CAN2->MCR |= ((1<<0)|(1<<4)|(1<<5));
    84.   CAN2->MCR &= ~(1<<16);//在调试时,CAN照常工作
    85.   
    86.   while(!(CAN2->MSR & 0xfffffffe))  //等待进入初始化模式
    87.   {
    88.     MyDebugger_LEDs(orange, on);
    89.   }
    90.   MyDebugger_LEDs(orange, off);
    91.   
    92.   /*
    93.   正常模式
    94.   重新同步跳跃宽度(1+1)tq
    95.   TS2[2:0]=3
    96.   TS1[3:0]=8
    97.   BRP[9:0]=2
    98.   
    99.   ps:
    100.   tq = (BRP[9:0] + 1) x tPCLK,
    101.   tBS2 = tq x (TS2[2:0] + 1),
    102.   tBS1 = tq x (TS1[3:0] + 1),
    103.   NominalBitTime = 1 × tq+tBS1+tBS2,
    104.   BaudRate = 1 / NominalBitTime
    105.   
    106.   波特率设为1M
    107.   */
    108.   CAN2->BTR = ((0<<30)|(0x01<<24)|(3<<20)|(8<<16)|(2<<0));
    109.   
    110.   CAN2->IER &= 0x00000000;
    111.   /*
    112.   FIFO1消息挂号中断使能
    113.   FIFO1满中断使能
    114.   FIFO1溢出中断使能
    115.   */
    116.   CAN2->IER |= ((1<<4)|(1<<5)|(1<<6));
    117.   
    118.   
    119.   NVIC->IP[65] = 0xa0;   //抢占优先级101,响应优先级0
    120.   NVIC->ISER[2] |= (1<<1);  //使能中断线65,也就是can2_rx1中断
    121.   
    122.   CAN2->MCR &= ~(0x00000001);//正常工作模式
    123.   
    124.   
    125.   //总共有28组过滤器
    126.   CAN1->FMR |= 1; //过滤器组工作在初始化模式
    127.   
    128.   CAN1->FMR &= 0xffffc0ff;//CAN2的过滤器组从14开始
    129.   CAN1->FMR |= (14<<8);
    130.   
    131.   CAN1->FM1R |= (1<<14);//过滤器组14的寄存器工作在标识符列表模式
    132.                        
    133.                         //位宽为16位,2个32位分为四个16位寄存器,过滤四个标识符
    134.   
    135.   //CAN1->FS1R |= (1<<15);//过滤器组15为单个32位寄存器,用于扩展标识符
    136.   
    137.   CAN1->FFA1R = 0x0fffc000;//0~13号过滤器组关联到fifo0,14~27号过滤器组关联到fifo1
    138.   
    139.   CAN1->FA1R &= ~(1<<14);//禁用过滤器组14
    140.   
    141.    /*
    142.   过滤器组0寄存器分为4个十六位过滤器:
    143.   标识符列表:
    144.   过滤器编号        匹配标准标识符             RTR       IDE           EXID[17:15]
    145.      0              0x7cb(111 1100 1011b)    数据帧    标准标识符     000b
    146.      1              0x4ab(100 1010 1011b)    数据帧    标准标识符     000b
    147.      2              0x7ab(111 1010 1011b)    数据帧    标准标识符     000b  
    148.      3              0x40b(100 0000 1011b)    数据帧    标准标识符     000b  
    149.   */
    150.   CAN1->sFilterRegister[14].FR1 &= 0x00000000;
    151.   CAN1->sFilterRegister[14].FR2 &= 0x00000000;
    152.   CAN1->sFilterRegister[14].FR1 |= ((0x7cb<<5)|(0<<4)|(0<<3));
    153.   CAN1->sFilterRegister[14].FR1 |= ((0x4ab<<21)|(0<<20)|(0<<19));
    154.   CAN1->sFilterRegister[14].FR2 |= ((0x7ab<<5)|(0<<4)|(0<<3));
    155.   CAN1->sFilterRegister[14].FR2 |= ((0x40b<<21)|(0<<20)|(0<<19));
    156.   
    157.   CAN1->FA1R |= (1<<14);//使能过滤器组14
    158.    
    159.   CAN1->FMR &= ~1; //过滤器组正常工作
    160.   
    161.   while(1)
    162.   {
    163.     /*
    164.     选择空的发送邮箱:
    165.     标准标识符0x7ab(111 1010 1011b)
    166.     数据帧
    167.     不使用扩展标识符
    168.     */
    169.     if( CAN1->TSR & ((1<<26)|(1<<27)|(1<<28)) )
    170.     {
    171.       empty_box = ((CAN1->TSR>>24) & 0x00000003);
    172.       CAN1->sTxMailBox[empty_box].TIR = (0x7ab<<21);  
    173.       
    174.       CAN1->sTxMailBox[empty_box].TDTR &= 0xfffffff0;
    175.       CAN1->sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8
    176.       
    177.       CAN1->sTxMailBox[empty_box].TDLR = 0x12345678;
    178.       
    179.       CAN1->sTxMailBox[empty_box].TDHR = 0x9abcdef0;
    180.       
    181.       CAN1->sTxMailBox[empty_box].TIR |= (1<<0);//请求发送
    182.     }
    183.     else
    184.     {
    185.       MyDebugger_LEDs(orange, on);
    186.     }
    187.     Delay(100);
    188.    
    189.      /*
    190.     选择空的发送邮箱:
    191.     标准标识符0x4ab(100 1010 1011b)
    192.     数据帧
    193.     不使用扩展标识符
    194.     */
    195.     if( CAN1->TSR & ((1<<26)|(1<<27)|(1<<28)) )
    196.     {
    197.       empty_box = ((CAN1->TSR>>24) & 0x00000003);
    198.       CAN1->sTxMailBox[empty_box].TIR = (0x4ab<<21);  
    199.       
    200.       CAN1->sTxMailBox[empty_box].TDTR &= 0xfffffff0;
    201.       CAN1->sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8
    202.       
    203.       CAN1->sTxMailBox[empty_box].TDLR = 0x56781234;
    204.       
    205.       CAN1->sTxMailBox[empty_box].TDHR = 0x9abcdef0;
    206.       
    207.       CAN1->sTxMailBox[empty_box].TIR |= (1<<0);//请求发送
    208.     }
    209.     else
    210.     {
    211.       MyDebugger_LEDs(orange, on);
    212.     }
    213.     Delay(100);
    214.    
    215.      /*
    216.     选择空的发送邮箱:
    217.     标准标识符0x7cb(100 1010 1011b)
    218.     数据帧
    219.     不使用扩展标识符
    220.     */
    221.     if( CAN1->TSR & ((1<<26)|(1<<27)|(1<<28)) )
    222.     {
    223.       empty_box = ((CAN1->TSR>>24) & 0x00000003);
    224.       CAN1->sTxMailBox[empty_box].TIR = (0x7cb<<21);  
    225.       
    226.       CAN1->sTxMailBox[empty_box].TDTR &= 0xfffffff0;
    227.       CAN1->sTxMailBox[empty_box].TDTR |= 0x00000006;//发送数据长度为6
    228.       
    229.       CAN1->sTxMailBox[empty_box].TDLR = 0x56781234;
    230.       
    231.       CAN1->sTxMailBox[empty_box].TDHR = 0x00009abc;
    232.       
    233.       CAN1->sTxMailBox[empty_box].TIR |= (1<<0);//请求发送
    234.     }
    235.     else
    236.     {
    237.       MyDebugger_LEDs(orange, on);
    238.     }
    239.     Delay(100);
    240.    
    241.      /*
    242.     选择空的发送邮箱:
    243.     标准标识符0x40b(100 0000 1011b)
    244.     数据帧
    245.     不使用扩展标识符
    246.     */
    247.     if( CAN1->TSR & ((1<<26)|(1<<27)|(1<<28)) )
    248.     {
    249.       empty_box = ((CAN1->TSR>>24) & 0x00000003);
    250.       CAN1->sTxMailBox[empty_box].TIR = (0x40b<<21);  
    251.       
    252.       CAN1->sTxMailBox[empty_box].TDTR &= 0xfffffff0;
    253.       CAN1->sTxMailBox[empty_box].TDTR |= 0x00000004;//发送数据长度为4
    254.       
    255.       CAN1->sTxMailBox[empty_box].TDLR = 0x56781234;
    256.       
    257.       CAN1->sTxMailBox[empty_box].TDHR = 0x00000000;
    258.       
    259.       CAN1->sTxMailBox[empty_box].TIR |= (1<<0);//请求发送
    260.     }
    261.     else
    262.     {
    263.       MyDebugger_LEDs(orange, on);
    264.     }
    265.     Delay(100);
    266.    
    267.    }
    268. }

    269. /****************************************
    270.   函数名:CAN_GPIO_config
    271.   参数:无
    272.   返回值:无
    273.   功能:设置CAN1,2控制器用到IO口
    274.   CAN1_TX---------PD1
    275.   CAN1_RX---------PB8
    276.   CAN2_TX---------PB13
    277.   CAN2_RX---------PB5
    278. ****************************************/
    279. void CAN_GPIO_config()
    280. {
    281.   RCC->AHB1ENR |= ((1<<1) | (1<<3));//使能GPIOB、D时钟
    282.   GPIOB->AFR[0] |= 0x00900000;      //AF9
    283.   GPIOB->AFR[1] |= 0x00900009;
    284.   GPIOD->AFR[0] |= 0x00000090;
    285.    
    286.   GPIOB->MODER &= 0xF3FCF3FF; //第二功能
    287.   GPIOB->MODER |= 0x08020800;
    288.   GPIOD->MODER &= 0xFFFFFFF3;
    289.   GPIOD->MODER |= 0x00000008;
    290.   
    291.   GPIOB->OSPEEDR &= 0xF3FCF3FF; //50M
    292.   GPIOB->OSPEEDR |= 0x08020800;
    293.   GPIOD->OSPEEDR &= 0xFFFFFFF3;
    294.   GPIOD->OSPEEDR |= 0x00000008;
    295.   
    296.   GPIOB->PUPDR &= 0xF3FCF3FF;   //上拉
    297.   GPIOB->PUPDR |= 0x04010400;
    298.   GPIOD->PUPDR &= 0xFFFFFFF3;
    299.   GPIOD->PUPDR |= 0x00000004;
    300. }

    301. /****************************************
    302.   函数名:CAN2_RX1_IRQHandler
    303.   参数:无
    304.   返回值:无
    305.   功能:CAN2fifo1接收中断处理
    306.         把信息存进循环队列
    307. ****************************************/
    308. void CAN2_RX1_IRQHandler()
    309. {
    310.   if(CAN2->RF1R & (0x00000003))//接收到新的消息,fifo1非空
    311.   {
    312.     Producer++;
    313.     if(Producer == RECEIVE_BUFFER_SIZE)Producer = 0;
    314.     if(Producer != Consumer)
    315.     {
    316.       CAN2_receive_buffer[Producer][0] = CAN2->sFIFOMailBox[1].RIR;
    317.       CAN2_receive_buffer[Producer][1] = CAN2->sFIFOMailBox[1].RDTR;
    318.       CAN2_receive_buffer[Producer][2] = CAN2->sFIFOMailBox[1].RDLR;
    319.       CAN2_receive_buffer[Producer][3] = CAN2->sFIFOMailBox[1].RDHR;
    320.     }
    321.     else
    322.     {
    323.       if(Producer == 0)Producer = RECEIVE_BUFFER_SIZE;
    324.       Producer--;
    325.       MyDebugger_LEDs(blue, on);
    326.     }   
    327.     CAN2->RF1R |= (1<<5);//释放邮箱
    328.   }
    329.   
    330.   if(CAN2->RF1R & (1<<3))//fifo0满
    331.   {
    332.     MyDebugger_LEDs(red, on);
    333.     CAN2->RF1R &= ~(1<<3);
    334.   }
    335.   
    336.   if(CAN2->RF1R & (1<<4))//fifo0溢出
    337.   {
    338.     MyDebugger_LEDs(red, on);
    339.     CAN2->RF1R &= ~(1<<4);
    340.   }
    341. }

    342. /****************************************
    343.   函数名:TIM7_init
    344.   参数:无
    345.   返回值:无
    346.   功能:初始化定时器7
    347.         作1s定时用
    348. ****************************************/
    349. void TIM7_init()
    350. {
    351.   RCC->APB1ENR |= (1<<5); //打开TIM7时钟
    352.   TIM7->PSC = 8399; //对时钟84M进行8400分频,使得计数频率为10k
    353.   TIM7->ARR = 10000;  //定时一秒
    354.   TIM7->CNT = 0;  //清空计数器
    355.   TIM7->CR1 |= (1<<7); //自动重装载预装载使能
    356.   TIM7->DIER |= 1; //使能中断
    357.   
    358.   NVIC->IP[55] = 0xe0;
    359.   NVIC->ISER[1] |= (1<<(55-32));
    360.   
    361.   TIM7->CR1 |= 1; //开始计时
    362. }

    363. /****************************************
    364.   函数名:TIM7_IRQHandler
    365.   参数:无
    366.   返回值:无
    367.   功能:定时器7中断处理
    368.         1s定时到
    369.         把can2收到的信息转换格式
    370.         用usrt发送到超级终端显示
    371. ****************************************/
    372. void TIM7_IRQHandler(void)
    373. {
    374.   u32 length;
    375.   if(TIM7->SR)
    376.   {
    377.     length = get_rece_data();
    378.     MyDebugger_Message( UART_send_buffer, length );
    379.     TIM7->SR &= ~(0x0001);
    380.   }
    381. }

    382. /****************************************
    383.   函数名:get_rece_data
    384.   参数:无
    385.   返回值:length 整理后要发送数据的长度
    386.   功能:把循环队列的信息取出
    387.         进行格式转换
    388.         把信息存到uart发送缓冲区
    389. ****************************************/
    390. u32 get_rece_data()
    391. {
    392.   u8 filter_No;
    393.   u8 Data_length;
    394.   char i;
    395.   u32 length = 0;
    396.   const char ascii[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
    397.                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    398.   while(1)
    399.   {
    400.     if(Producer != Consumer)
    401.       {
    402.         Consumer++;
    403.         if(Consumer == RECEIVE_BUFFER_SIZE)Consumer=0;
    404.         
    405.         UART_send_buffer[length++] = '\n';
    406.         UART_send_buffer[length++] = '\r';
    407.         //Filter No.xx
    408.         UART_send_buffer[length++] = 'F';
    409.         UART_send_buffer[length++] = 'i';
    410.         UART_send_buffer[length++] = 'l';
    411.         UART_send_buffer[length++] = 't';
    412.         UART_send_buffer[length++] = 'e';
    413.         UART_send_buffer[length++] = 'r';
    414.         UART_send_buffer[length++] = ' ';
    415.         UART_send_buffer[length++] = 'N';
    416.         UART_send_buffer[length++] = 'o';
    417.         UART_send_buffer[length++] = '.';
    418.    
    419.         filter_No = (CAN2_receive_buffer[Consumer][1]>>8) & 0x000000ff;
    420.         UART_send_buffer[length++] = filter_No%100/10 + '0';
    421.         UART_send_buffer[length++] = filter_No%10 + '0';
    422.         UART_send_buffer[length++] = '\n';
    423.         UART_send_buffer[length++] = '\r';
    424.         
    425.         //DataLength:x
    426.         UART_send_buffer[length++] = 'D';
    427.         UART_send_buffer[length++] = 'a';
    428.         UART_send_buffer[length++] = 't';
    429.         UART_send_buffer[length++] = 'a';
    430.         UART_send_buffer[length++] = 'L';
    431.         UART_send_buffer[length++] = 'e';
    432.         UART_send_buffer[length++] = 'n';
    433.         UART_send_buffer[length++] = 'g';
    434.         UART_send_buffer[length++] = 't';
    435.         UART_send_buffer[length++] = 'h';
    436.         UART_send_buffer[length++] = ':';
    437.         Data_length = CAN2_receive_buffer[Consumer][1] & 0x0000000f;
    438.         UART_send_buffer[length++] = Data_length % 10 + '0';
    439.         UART_send_buffer[length++] = '\n';
    440.         UART_send_buffer[length++] = '\r';
    441.         
    442.         
    443.         if(CAN2_receive_buffer[Consumer][0] & (1<<1))
    444.         {
    445.           UART_send_buffer[length++] = 'R';
    446.           UART_send_buffer[length++] = 'e';
    447.           UART_send_buffer[length++] = 'm';
    448.           UART_send_buffer[length++] = 'o';
    449.           UART_send_buffer[length++] = 't';
    450.           UART_send_buffer[length++] = 'e';
    451.           UART_send_buffer[length++] = 'F';
    452.           UART_send_buffer[length++] = 'r';
    453.           UART_send_buffer[length++] = 'a';
    454.           UART_send_buffer[length++] = 'm';
    455.           UART_send_buffer[length++] = 'e';
    456.         }
    457.         else
    458.         {
    459.           UART_send_buffer[length++] = 'D';
    460.           UART_send_buffer[length++] = 'a';
    461.           UART_send_buffer[length++] = 't';
    462.           UART_send_buffer[length++] = 'a';
    463.           UART_send_buffer[length++] = 'F';
    464.           UART_send_buffer[length++] = 'r';
    465.           UART_send_buffer[length++] = 'a';
    466.           UART_send_buffer[length++] = 'm';
    467.           UART_send_buffer[length++] = 'e';
    468.         }
    469.         UART_send_buffer[length++] = '\n';
    470.         UART_send_buffer[length++] = '\r';        
    471.         
    472.         
    473.         if(CAN2_receive_buffer[Consumer][0] & (1<<2))
    474.         {
    475.           UART_send_buffer[length++] = 'e';
    476.           UART_send_buffer[length++] = 'x';
    477.           UART_send_buffer[length++] = 't';
    478.           UART_send_buffer[length++] = ' ';
    479.           UART_send_buffer[length++] = 'I';
    480.           UART_send_buffer[length++] = 'D';
    481.           UART_send_buffer[length++] = ':';
    482.          
    483.           UART_send_buffer[length++] =
    484.             ascii[CAN2_receive_buffer[Consumer][0] >> 31];
    485.           UART_send_buffer[length++] =
    486.             ascii[(CAN2_receive_buffer[Consumer][0] >> 27)& 0x0000000f];
    487.           UART_send_buffer[length++] =
    488.             ascii[(CAN2_receive_buffer[Consumer][0] >> 23)& 0x0000000f];
    489.           UART_send_buffer[length++] =
    490.             ascii[(CAN2_receive_buffer[Consumer][0] >> 19)& 0x0000000f];
    491.           UART_send_buffer[length++] =
    492.             ascii[(CAN2_receive_buffer[Consumer][0] >> 15)& 0x0000000f];
    493.           UART_send_buffer[length++] =
    494.             ascii[(CAN2_receive_buffer[Consumer][0] >> 11)& 0x0000000f];
    495.           UART_send_buffer[length++] =
    496.             ascii[(CAN2_receive_buffer[Consumer][0] >> 7)& 0x0000000f];
    497.           UART_send_buffer[length++] =
    498.             ascii[(CAN2_receive_buffer[Consumer][0] >> 3)& 0x0000000f];
    499.         }
    500.         else
    501.         {
    502.           UART_send_buffer[length++] = 's';
    503.           UART_send_buffer[length++] = 't';
    504.           UART_send_buffer[length++] = 'd';
    505.           UART_send_buffer[length++] = ' ';
    506.           UART_send_buffer[length++] = 'I';
    507.           UART_send_buffer[length++] = 'D';
    508.           UART_send_buffer[length++] = ':';
    509.          
    510.           UART_send_buffer[length++] =
    511.             ascii[CAN2_receive_buffer[Consumer][0] >> 29];
    512.           UART_send_buffer[length++] =
    513.             ascii[(CAN2_receive_buffer[Consumer][0] >> 25)& 0x0000000f];
    514.           UART_send_buffer[length++] =
    515.             ascii[(CAN2_receive_buffer[Consumer][0] >> 21)& 0x0000000f];        
    516.         }
    517.         UART_send_buffer[length++] = '\n';
    518.         UART_send_buffer[length++] = '\r';
    519.         
    520.         UART_send_buffer[length++] = 'D';
    521.         UART_send_buffer[length++] = 'a';
    522.         UART_send_buffer[length++] = 't';
    523.         UART_send_buffer[length++] = 'a';
    524.         UART_send_buffer[length++] = ':';
    525.         if(Data_length > 4)
    526.         {
    527.           for(i = 2*Data_length - 8; i > 0; i--)
    528.             UART_send_buffer[length++] =
    529.               ascii[(CAN2_receive_buffer[Consumer][3] >> ((i-1)*4))& 0x0000000f];
    530.          
    531.           for(i = 8; i > 0; i--)
    532.             UART_send_buffer[length++] =
    533.               ascii[(CAN2_receive_buffer[Consumer][2] >> ((i-1)*4))& 0x0000000f];
    534.         }
    535.         else
    536.         {
    537.           for(i = 2*Data_length; i > 0; i--)
    538.             UART_send_buffer[length++] =
    539.               ascii[(CAN2_receive_buffer[Consumer][2] >> ((i-1)*4))& 0x0000000f];         
    540.         }
    541.         UART_send_buffer[length++] = '\n';
    542.         UART_send_buffer[length++] = '\r';
    543.       }
    544.     else
    545.       break;
    546.   }
    547.   return length;
    548. }


    549. void Delay(uint32_t nTime)
    550. {
    551.   Gb_TimingDelay = nTime;

    552.   while(Gb_TimingDelay != 0);
    553. }

    554. void SysTick_Handler(void)
    555. {
    556.   if (Gb_TimingDelay != 0x00)
    557.   {
    558.     Gb_TimingDelay--;
    559.   }
    560. }
    复制代码
    运行结果:
    1347072056_2702.png
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-8-7 21:35
  • 签到天数: 340 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2013-7-26 11:36:08 | 显示全部楼层
    弄了这么多东西上去,奋斗哥幸苦了。不过貌似电子版很多人都有吧…………
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 13:34:57 | 显示全部楼层
    kk5290122 发表于 2013-7-26 11:36
    弄了这么多东西上去,奋斗哥幸苦了。不过貌似电子版很多人都有吧………… ...

    电子版?分享看看撒~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-8-7 21:35
  • 签到天数: 340 天

    连续签到: 1 天

    [LV.8]以坛为家I

    发表于 2013-7-26 13:55:20 | 显示全部楼层
    奋斗哥 发表于 2013-7-26 13:34
    电子版?分享看看撒~

    就这个呀…………

    我的stm32f4学习历程.pdf (5.32 MB, 下载次数: 904)
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

     楼主| 发表于 2013-7-26 14:14:52 | 显示全部楼层
    kk5290122 发表于 2013-7-26 13:55
    就这个呀…………

    哈哈,我上传过都忘记了{:soso_e143:} 展现出来大家也看看吧{:soso_e104:}
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2014-7-24 09:00
  • 签到天数: 205 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2013-7-28 02:15:04 | 显示全部楼层
    不愧是奋斗哥。。。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-30 16:52 , Processed in 0.209980 second(s), 32 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.