用音量控制器介绍GD32F330C8T6单片机外设应用(一)——编码开关、音量控制芯片M62429

系列文章目录

第一章 GD32F330单片机、编码开关、音量控制芯片功能介绍
第二章 整机硬件原理介绍
第三章 程序代码详解和调试过程介绍



前言

学习单片机应该选择最新、最流行、最好买的型号,国产的兆易创新的GD32F330/350系列出货量大、价格便宜,是很好的替代STM32的选型方案。

通过实际产品设计过程,除了能对单片机资源有的放矢地了解,还能有多种收获:电路设计技巧、需求分析取舍、以及结构设计知识,等等不一而足。

本系列章节将介绍音量控制器的设计制造过程,通过编码器开关、音量控制芯片M62429这两个外设元件,介绍GD32F330单片机的使用方法、资源调用和设计技巧。


一、音量控制芯片M62429

音量控制芯片M62429

1、基本功能和逻辑框图

M62429是双声道音量控制器,和传统机械旋钮的不同,它是通过串行数据控制的来实现音量加减的。该芯片可用于音响系统的前级,比起传统旋钮好处很多:没有机械旋钮触片老化的问题,也就没有了机械噪声;随着电路板设计而成,可以减少走线路径,简化电路规模。
M62429的功能框图
下图是M62429的逻辑框图,芯片内部给每个声道都设有独立运算放大器,其基准电压通过外部电源获得,其放大增益由逻辑控制单元控制。
在这里插入图片描述

2、仅仅靠时钟和数据两条线控制音量和选择声道

音量控制指的就是对输入信号的衰减控制,所谓把音量调大,就是将衰减调小,调到最大,就是完全无衰减;调到最小,就是衰减最大。

控制码的D2到D6位可以以4dB步进对信号衰减,如表所示,从0dB衰减到-80dB,加上完全衰减,一共可以有21个位置可选择。

在4dB以下,还有三个1dB步进的精确选择,由D7、D8两位决定。分贝的变化是非线性的,这与人的主观听感的非线性特性相弥合。

D0位选择控制哪个声道,D1位可选择是否单独控制一个声道,还是两个一起控制;

D9,D10位是控制有效位,当这两位被置位时,控制方能生效。

芯片的数据输入格式和音量控制码
M62429在时钟位的下降沿触发,或者说置位信号;在时钟的上升沿可以读取当前的数据信号。
串行数据和时钟的关系

3、需要特别注意的交流特性

时钟周期至少4us;高低电平分别至少1.6us;因此在设计程序时,须给出2倍的时间冗余是上策。

时钟上升和下降时间不能超过0.4us,这是对信号品质提了要求,除了时钟速率不能过快,信号走线也应设计考究;

改变数据的时间和数据维持的时间分别至少要0.8us,这在写控制代码时要注意留足时间间隔以便让芯片能响应成功。
数字时钟的交流特性
市面常见的封装类型是SO-8,有关该芯片的电路设计,后续章节会详细介绍。

二、旋转编码开关

1、编码开关种类多样但原理基本相同

在电机测速上使用的旋转编码器,有磁信号式的,有光电式的,通过两个脉冲信号A和B的相位差判断旋转方向、测量电机转速等,电机上的编码器可以做到每圈10000个脉冲信号;
旋转编码器

在人机交互方面使用的旋转编码器,一般叫做编码开关,其原理与电机上用的基本相同,每圈脉冲信号数量只有几十个脉冲,且旋转轴与信号端是接触式的,转起来有顿挫感。
编码开关
编码开关

凡是用旋转方式改变一个数量大小的地方,都可以用到编码开关,常见于示波器上、滚筒洗衣机上、汽车上、鼠标上。

它转起来“没头没尾”,因此可以通过程序人为设定起始;它虽然一圈只有十几个到几十个步进,但是程序上可以任意改变变量的变化程度(鼠标的滚轮就是一个好例子:可以设置轻轻滚一下就能翻好几页)。可以说,只要能提供稳定可靠的旋转方向信息,我们就可以发挥想象空间,通过编码开关做出各种各样的产品功能来。

2、功能实现方式

有三个主要的引脚,A、B和C,分别给AB引脚各加上拉电阻,以C做参考端,正转一个步进时,就能观察到A-C和B-C各有一个脉冲信号出现,且A信号超前B信号90°,反转时A信号落后B信号90° ,通过AB两个信号超前还是滞后,就能判断旋钮的旋转方向;

同时,旋钮一个步进,AB两信号将会经历完整的信号周期,在信号周期中,AB两个信号的高低电平将会有规律地重合。利用这个规律,可以得知正转还是反转。

旋转一个步进,AB将会重合两次
部分代码如下,这段代码的关键点:

1、获取开关没有旋转时候的信号稳定状态;
2、获取旋转过程中的两信号电平状态,这个过程包括AB两信号的四个交互状态;
3、判断旋转方向后,把要执行的内容写在函数里面,或者给出特定返回参数,供其他函数使用;


    // 以下是编码开关的方向判断程序段

    if(KEY2)                 //buttonA代表编码开关旋转是产生的信号A
    {
        if(KEY3)              //buttonB为信号B
        {
            input_status = 1;     //A=1;B=1  ;input_status用1,2,3,4代表两信号的状态
        }
        else if(!KEY3)
        {
            input_status = 4;     //A=1;B=0
        }
    }
    else if(!KEY2)
    {
        if(KEY3)
        {
            input_status = 2;      //A=0;B=1
        }
        else if(!KEY3)
        {
            input_status = 3; 	    //A=0;B=0
        }
    }

    //通过判断两个状态是否相等来 判断状态确实有变化(类似于按键消抖过程)
    if(input_status == input_status0)
    {
        if(fun_one == 0)   //如果第一次执行该函数
        {
            fun_one = 1;
            scanf_status = input_status;
        }
        input_status0 = input_status;
        //如果状态为 1 因为开关旋转后 电平会停在 1 状态,在此执行所需要的东西
        if(scanf_status == 1)	  //若scanf_status为1,说明旋转开关正向旋转了一个步进(20个步进是一圈)
        {
            if(pulse0 >= 4)
            {
                pulse0 = 0;
                resolution++;
                LED01_TOGGLE();
                //开关正旋转一次写在这里就行了
                //需要执行的语句
                if (resolution >= 100)
                {
                    resolution = 100;
                }
            }
            if(pulse1 >= 4)
            {
                pulse1 = 0;
                resolution--;
                if (resolution <= 0)
                {
                    resolution = 0;
                }
                LED01_TOGGLE();
                //开关反旋转一次写在这里就行了
                //需要执行的语句
            }

            //在状态1 的 前提下,判断是正旋转还是反旋转

            if (input_status == 2)        //正旋转
            {
                scanf_status = 2;
                pulse0++;
                pulse1 = 0;
            }
            else if (input_status == 4)        //反旋转
            {
                scanf_status = 4;
                pulse1++;
                pulse0 = 0;
            }
        }

        //在状态2 的 前提下,判断是正旋转还是反旋转

        if (scanf_status == 2)        //正旋转
        {
            if (input_status == 3)        //正旋转
            {
                scanf_status = 3;
                pulse0++;
                pulse1 = 0;
            }
            if (input_status == 1)        //反旋转
            {
                scanf_status = 1;
                pulse1++;
                pulse0 = 0;
            }
        }


        if (scanf_status == 3)        //在状态 3 的 前提下,判断是正旋转还是反旋转
        {
            if (input_status == 4)        //正旋转
            {
                scanf_status = 4;
                pulse0++;
                pulse1 = 0;
            }
            if (input_status == 2)        //反旋转
            {
                scanf_status = 2;
                pulse1++;
                pulse0 = 0;
            }
        }
        if (scanf_status == 4)         //在状态 4 的 前提下,判断是正旋转还是反旋转
        {
            if (input_status == 1)        //正旋转
            {
                scanf_status = 1;
                pulse0++;
                pulse1 = 0;
            }
            if (input_status == 3)        //反旋转
            {
                scanf_status = 3;
                pulse1++;
                pulse0 = 0;
            }
        }
    }
    else
    {
        input_status0 = input_status;
    }

三、GD32F330C8T6单片机

笔者在【用18B20温度控制板介绍华大HC32F030单片机 (四)——由华大HC32F030换成兆易创新GD32F330】这篇文章中,介绍了使用GD32F330C8T6的理由:配置主流、实用性广、备件易得,理应继续深入研究该芯片。

1、旋转编码开关使用到的资源

旋转编码开关除了两个方向信号A B外,还有一个按键信号,其中AB方向信号使用外中断应该会更加方便处理。

理由:
①这两个信号规律性强,旋转一个步进信号周期变化小,只要考虑采集信号的电平还是上升或者下降沿,这是外中断的长项;
②可以将外中断优先级适当调高,力求人机交互响应快速。

GD32F330的IO引脚大多都能做外中断用,可以考虑另外一个按键信号使用外中断,也可以用扫描方式——可以借助定时器,也可以在主程序里扫描。

旋转编码开关
如图,上面三个脚是A、B、C脚,下面两个是按键信号引脚,按下旋转轴就可以。

2、M62429芯片用到的资源

该芯片的时钟信号只有一个频率,在时钟上升沿就是读数据,在下降沿就是写数据。

首先需要单片机产生稳定的时钟信号,可以调用定时器的PWM功能,除了频率可以设置,还可以改变占空比,然后再获取这个PWM信号,在其上升沿或者下降沿出现时机将数据信号给出。

也可以不产出时钟信号,在写数据的同时,操作单片机时钟管脚位的置位或者复位,满足芯片提出的时钟-数据协议要求即可。


总结

1、首先明确一点是,务必通过实例设计过程,尽可能多地了解单片机资源的使用;
2、产品就是音量控制器,更加丰富的功能需求,可以边做边分析;

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GD32F330是一款高性能的32位微控制器,支持多种外设驱动。要驱动蜂鸣器,可以使用GD32F330的定时器和通用脉冲宽度调制(PWM)模块。 具体步骤如下: 1. 配置GPIO口,将蜂鸣器连接到GPIO口。 2. 配置定时器,设置其工作模式为PWM模式,同时设置PWM占空比,使得输出电平变化频率在人耳可接受范围内。 3. 启动定时器,使得PWM波形输出到GPIO口,从而驱动蜂鸣器。 以下是一个简单的代码示例: ``` #include "gd32f3x0.h" void buzzer_init(void) { rcu_periph_clock_enable(RCU_GPIOB); gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0); rcu_periph_clock_enable(RCU_TIMER0); timer_oc_parameter_struct timer_ocinitpara; timer_parameter_struct timer_initpara; timer_struct_para_init(&timer_initpara); timer_deinit(TIMER0); /* initialize TIMER init parameter struct */ timer_initpara.prescaler = 119; // 时钟预分频值 timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 799; // PWM周期 timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_init(TIMER0, &timer_initpara); /* initialize TIMER channel output parameter struct */ timer_channel_output_struct_para_init(&timer_ocinitpara); timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH; // 输出高电平 timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocinitpara.outputstate = TIMER_CCX_ENABLE; // 使能输出 timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_HIGH; timer_channel_output_config(TIMER0, TIMER_CH_1, &timer_ocinitpara); /* enable TIMER counter */ timer_enable(TIMER0); } int main(void) { buzzer_init(); while (1) { // do something else } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值