stm32开发之wm8978驱动记录(rt-thread)

头文件

//
// Created by shchl on 2024/3/11.
//

#ifndef F4_OS_AUDIO_WM8978_H
#define F4_OS_AUDIO_WM8978_H

#include "rtdevice.h"

#define WM8978_ADDR        0X1A    //WM8978的器件地址,固定为0X1A(7bit 地址)
struct audio_wm8978 {
    struct rt_i2c_bus_device *bus; /*总线指针*/
    uint8_t addr; /*地址*/
    rt_mutex_t lock; /*互斥锁*/
};

typedef struct audio_wm8978 *audio_wm8978_t;

audio_wm8978_t wm8978_init(char *bus_name, uint8_t addr);


rt_err_t wm8978_cnf_init();

void wm8978_deInit(audio_wm8978_t wm8978);

rt_err_t wm8978_set_reg(audio_wm8978_t wm8978, uint8_t reg, uint16_t val);

uint16_t wm8978_read_reg(uint8_t reg);

void wm8978_ADDA_Cfg(uint8_t dacen, uint8_t adcen);

void wm8978_Input_Cfg(uint8_t micen, uint8_t lineinen, uint8_t auxen);

void wm8978_Output_Cfg(uint8_t dacen, uint8_t bpsen);

void wm8978_MIC_Gain(uint8_t gain);

void wm8978_LINEIN_Gain(uint8_t gain);

void wm8978_AUX_Gain(uint8_t gain);

void wm8978_I2S_Cfg(uint8_t fmt, uint8_t len);

void wm8978_HPvol_Set(uint8_t voll, uint8_t volr);

void wm8978_SPKvol_Set(uint8_t volx);

void wm8978_3D_Set(uint8_t depth);

void wm8978_EQ_3D_Dir(uint8_t dir);

void wm8978_EQ1_Set(uint8_t cfreq, uint8_t gain);

void wm8978_EQ2_Set(uint8_t cfreq, uint8_t gain);

void wm8978_EQ3_Set(uint8_t cfreq, uint8_t gain);

void wm8978_EQ4_Set(uint8_t cfreq, uint8_t gain);

void wm8978_EQ5_Set(uint8_t cfreq, uint8_t gain);

#endif //F4_OS_AUDIO_WM8978_H

源文件

//
// Created by shchl on 2024/3/11.
//
#include "audio_wm8978.h"
#include <rtthread.h>

#define DBG_ENABLE
#define DBG_TAG "wm8978"
#define DBG_LVL DBG_LOG

#include <rtdbg.h>

static audio_wm8978_t device = NULL;
//WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存
//因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值
//写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值.
//注意:WM8978的寄存器值是9位的,所以要用uint16_t来存储.
static uint16_t wm8978_REGVAL_TBL[58] =
        {
                0X0000, 0X0000, 0X0000, 0X0000, 0X0050, 0X0000, 0X0140, 0X0000,
                0X0000, 0X0000, 0X0000, 0X00FF, 0X00FF, 0X0000, 0X0100, 0X00FF,
                0X00FF, 0X0000, 0X012C, 0X002C, 0X002C, 0X002C, 0X002C, 0X0000,
                0X0032, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000,
                0X0038, 0X000B, 0X0032, 0X0000, 0X0008, 0X000C, 0X0093, 0X00E9,
                0X0000, 0X0000, 0X0000, 0X0000, 0X0003, 0X0010, 0X0010, 0X0100,
                0X0100, 0X0002, 0X0001, 0X0001, 0X0039, 0X0039, 0X0039, 0X0039,
                0X0001, 0X0001
        };

static rt_err_t write_reg(uint8_t reg, uint16_t val) {
    return wm8978_set_reg(device, reg, val);
}

rt_err_t wm8978_set_reg(audio_wm8978_t wm8978, uint8_t reg, uint16_t val) {
    rt_uint8_t buf[2];
    buf[0] = (reg << 1) | ((val >> 8) & 0X01);
    buf[1] = val & 0XFF;
    if (wm8978 == NULL) {
        LOG_E("请先执行 wm8978_init");
        return RT_ERROR;
    }

    if (rt_i2c_master_send(wm8978->bus, wm8978->addr, RT_I2C_WR, buf, 2) != 2) {
        return RT_ERROR;
    }
    wm8978_REGVAL_TBL[reg] = val;    //保存寄存器值到本地
    return RT_EOK;
}

/**
 * 初始化 wm8978
 * @param bus_name 总线名
 * @param addr 地址(7位地址)
 * @return
 */
audio_wm8978_t wm8978_init(char *bus_name, uint8_t addr) {
    RT_ASSERT(bus_name);
    device = rt_calloc(1, sizeof(struct audio_wm8978));
    if (device == RT_NULL) {
        LOG_E("Can't allocate memory for audio_wm8978 device on '%s' ", bus_name);
        return RT_NULL;
    }
    device->bus = rt_i2c_bus_device_find(bus_name);

    if (device->bus == RT_NULL) {
        LOG_E("Can't find audio_wm8978 device on '%s' ", bus_name);
        rt_free(device);
        return RT_NULL;
    }

    device->lock = rt_mutex_create("mutex_wm8978", RT_IPC_FLAG_FIFO);
    if (device->lock == RT_NULL) {
        LOG_E("Can't create mutex for audio_wm8978 device on '%s' ", bus_name);
        rt_free(device);
        return RT_NULL;
    }
    {
        device->bus->timeout = 3;/*尝试3次*/
        device->addr = addr;
    }


    return device;
}

void wm8978_deInit(audio_wm8978_t wm8978) {
    if (wm8978 != NULL) {
        if (wm8978 == device) {
            rt_free(wm8978);
        }
    }
}

/**
 * 配置初始化
 * @param wm8978
 */
rt_err_t wm8978_cnf_init() {

    if (write_reg(0, 0) != RT_EOK) return RT_ERROR;            //软复位WM8978发送指令失败,WM8978异常
    //以下为通用设置
    write_reg(1, 0X1B);    //R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)
    write_reg(2, 0X1B0);    //R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能
    write_reg(3, 0X6C);    //R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能
    write_reg(6, 0);        //R6,MCLK由外部提供
    write_reg(43, 1 << 4);    //R43,INVROUT2反向,驱动喇叭
    write_reg(47, 1 << 8);    //R47设置,PGABOOSTL,左通道MIC获得20倍增益
    write_reg(48, 1 << 8);    //R48设置,PGABOOSTR,右通道MIC获得20倍增益
    write_reg(49, 1 << 1);    //R49,TSDEN,开启过热保护
    write_reg(49, 1 << 2);    //R49,SPEAKER BOOST,1.5x
    write_reg(10, 1 << 3);    //R10,SOFTMUTE关闭,128x采样,最佳SNR
    write_reg(14, 1 << 3);    //R14,ADC 128x采样率
    return RT_EOK;
}

//WM8978读寄存器
//就是读取本地寄存器值缓冲区内的对应值
//reg:寄存器地址 
//返回值:寄存器值
uint16_t wm8978_read_reg(uint8_t reg) {
    return wm8978_REGVAL_TBL[reg];
}

//WM8978 DAC/ADC配置
//adcen:adc使能(1)/关闭(0)
//dacen:dac使能(1)/关闭(0)
void wm8978_ADDA_Cfg(uint8_t dacen, uint8_t adcen) {
    uint16_t regval;
    regval = wm8978_read_reg(3);   //读取R3
    if (dacen)regval |= 3 << 0;        //R3最低2个位设置为1,开启DACR&DACL
    else regval &= ~(3 << 0);           //R3最低2个位清零,关闭DACR&DACL.
    write_reg(3, regval);       //设置R3
    regval = wm8978_read_reg(2);    //读取R2
    if (adcen)regval |= 3 << 0;         //R2最低2个位设置为1,开启ADCR&ADCL
    else regval &= ~(3 << 0);           //R2最低2个位清零,关闭ADCR&ADCL.
    write_reg(2, regval);       //设置R2
}

//WM8978 输入通道配置
//micen:MIC开启(1)/关闭(0)
//lineinen:Line In开启(1)/关闭(0)
//auxen:aux开启(1)/关闭(0) 
void wm8978_Input_Cfg(uint8_t micen, uint8_t lineinen, uint8_t auxen) {
    uint16_t regval;
    regval = wm8978_read_reg(2);    //读取R2
    if (micen)regval |= 3 << 2;        //开启INPPGAENR,INPPGAENL(MIC的PGA放大)
    else regval &= ~(3 << 2);        //关闭INPPGAENR,INPPGAENL.
    write_reg(2, regval);    //设置R2

    regval = wm8978_read_reg(44);    //读取R44
    if (micen)regval |= 3 << 4 | 3 << 0;    //开启LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
    else regval &= ~(3 << 4 | 3 << 0);    //关闭LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
    write_reg(44, regval);//设置R44

    if (lineinen)wm8978_LINEIN_Gain(5);//LINE IN 0dB增益
    else wm8978_LINEIN_Gain(0);    //关闭LINE IN
    if (auxen)wm8978_AUX_Gain(7);//AUX 6dB增益
    else wm8978_AUX_Gain(0);    //关闭AUX输入
}

//WM8978 输出配置
//dacen:DAC输出(放音)开启(1)/关闭(0)
//bpsen:Bypass输出(录音,包括MIC,LINE IN,AUX等)开启(1)/关闭(0) 
void wm8978_Output_Cfg(uint8_t dacen, uint8_t bpsen) {
    uint16_t regval = 0;
    if (dacen)regval |= 1 << 0;    //DAC输出使能
    if (bpsen) {
        regval |= 1 << 1;        //BYPASS使能
        regval |= 5 << 2;        //0dB增益
    }
    write_reg(50, regval);//R50设置
    write_reg(51, regval);//R51设置
}

//WM8978 MIC增益设置(不包括BOOST的20dB,MIC-->ADC输入部分的增益)
//gain:0~63,对应-12dB~35.25dB,0.75dB/Step
void wm8978_MIC_Gain(uint8_t gain) {
    gain &= 0X3F;
    write_reg(45, gain);        //R45,左通道PGA设置
    write_reg(46, gain | 1 << 8);    //R46,右通道PGA设置
}

//WM8978 L2/R2(也就是Line In)增益设置(L2/R2-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void wm8978_LINEIN_Gain(uint8_t gain) {
    uint16_t regval;
    gain &= 0X07;
    regval = wm8978_read_reg(47);    //读取R47
    regval &= ~(7 << 4);            //清除原来的设置
    write_reg(47, regval | gain << 4);//设置R47
    regval = wm8978_read_reg(48);    //读取R48
    regval &= ~(7 << 4);            //清除原来的设置
    write_reg(48, regval | gain << 4);//设置R48
}

//WM8978 AUXR,AUXL(PWM音频部分)增益设置(AUXR/L-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void wm8978_AUX_Gain(uint8_t gain) {
    uint16_t regval;
    gain &= 0X07;
    regval = wm8978_read_reg(47);    //读取R47
    regval &= ~(7 << 0);            //清除原来的设置
    write_reg(47, regval | gain << 0);//设置R47
    regval = wm8978_read_reg(48);    //读取R48
    regval &= ~(7 << 0);            //清除原来的设置
    write_reg(48, regval | gain << 0);//设置R48
}

//设置I2S工作模式
//fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP;
//len:0,16位;1,20位;2,24位;3,32位;  
void wm8978_I2S_Cfg(uint8_t fmt, uint8_t len) {
    fmt &= 0X03;
    len &= 0X03;//限定范围
    write_reg(4, (fmt << 3) | (len << 5));    //R4,WM8978工作模式设置
}

//设置耳机左右声道音量
//voll:左声道音量(0~63)
//volr:右声道音量(0~63)
void wm8978_HPvol_Set(uint8_t voll, uint8_t volr) {
    voll &= 0X3F;
    volr &= 0X3F;//限定范围
    if (voll == 0)voll |= 1 << 6;//音量为0时,直接mute
    if (volr == 0)volr |= 1 << 6;//音量为0时,直接mute
    write_reg(52, voll);            //R52,耳机左声道音量设置
    write_reg(53, volr | (1 << 8));    //R53,耳机右声道音量设置,同步更新(HPVU=1)
}

//设置喇叭音量
//voll:左声道音量(0~63) 
void wm8978_SPKvol_Set(uint8_t volx) {
    volx &= 0X3F;//限定范围
    if (volx == 0)volx |= 1 << 6;//音量为0时,直接mute
    write_reg(54, volx);            //R54,喇叭左声道音量设置
    write_reg(55, volx | (1 << 8));    //R55,喇叭右声道音量设置,同步更新(SPKVU=1)
}

//设置3D环绕声
//depth:0~15(3D强度,0最弱,15最强)
void wm8978_3D_Set(uint8_t depth) {
    depth &= 0XF;//限定范围
    write_reg(41, depth);    //R41,3D环绕设置
}

//设置EQ/3D作用方向
//dir:0,在ADC起作用
//    1,在DAC起作用(默认)
void wm8978_EQ_3D_Dir(uint8_t dir) {
    uint16_t regval;
    regval = wm8978_read_reg(0X12);
    if (dir)regval |= 1 << 8;
    else regval &= ~(1 << 8);
    write_reg(18, regval);//R18,EQ1的第9位控制EQ/3D方向
}

//设置EQ1
//cfreq:截止频率,0~3,分别对应:80/105/135/175Hz
//gain:增益,0~24,对应-12~+12dB
void wm8978_EQ1_Set(uint8_t cfreq, uint8_t gain) {
    uint16_t regval;
    cfreq &= 0X3;//限定范围
    if (gain > 24)gain = 24;
    gain = 24 - gain;
    regval = wm8978_read_reg(18);
    regval &= 0X100;
    regval |= cfreq << 5;    //设置截止频率
    regval |= gain;        //设置增益
    write_reg(18, regval);//R18,EQ1设置
}

//设置EQ2
//cfreq:中心频率,0~3,分别对应:230/300/385/500Hz
//gain:增益,0~24,对应-12~+12dB
void wm8978_EQ2_Set(uint8_t cfreq, uint8_t gain) {
    uint16_t regval = 0;
    cfreq &= 0X3;//限定范围
    if (gain > 24)gain = 24;
    gain = 24 - gain;
    regval |= cfreq << 5;    //设置截止频率
    regval |= gain;        //设置增益
    write_reg(19, regval);//R19,EQ2设置
}

//设置EQ3
//cfreq:中心频率,0~3,分别对应:650/850/1100/1400Hz
//gain:增益,0~24,对应-12~+12dB
void wm8978_EQ3_Set(uint8_t cfreq, uint8_t gain) {
    uint16_t regval = 0;
    cfreq &= 0X3;//限定范围
    if (gain > 24)gain = 24;
    gain = 24 - gain;
    regval |= cfreq << 5;    //设置截止频率
    regval |= gain;        //设置增益
    write_reg(20, regval);//R20,EQ3设置
}

//设置EQ4
//cfreq:中心频率,0~3,分别对应:1800/2400/3200/4100Hz
//gain:增益,0~24,对应-12~+12dB
void wm8978_EQ4_Set(uint8_t cfreq, uint8_t gain) {
    uint16_t regval = 0;
    cfreq &= 0X3;//限定范围
    if (gain > 24)gain = 24;
    gain = 24 - gain;
    regval |= cfreq << 5;    //设置截止频率
    regval |= gain;        //设置增益
    write_reg(21, regval);//R21,EQ4设置
}

//设置EQ5
//cfreq:中心频率,0~3,分别对应:5300/6900/9000/11700Hz
//gain:增益,0~24,对应-12~+12dB
void wm8978_EQ5_Set(uint8_t cfreq, uint8_t gain) {
    uint16_t regval = 0;
    cfreq &= 0X3;//限定范围
    if (gain > 24)gain = 24;
    gain = 24 - gain;
    regval |= cfreq << 5;    //设置截止频率
    regval |= gain;        //设置增益
    write_reg(22, regval);//R22,EQ5设置
}

### 实现 STM32 的 LCD 菜单界面 要在 STM32 平台上通过 LCD 显示屏实现菜单功能,可以借助图形库 UCGUI 来简化开发流程。UCGUI 是一款轻量级的嵌入式 GUI 库,适用于资源受限的微控制器环境。以下是具体方法: #### 配置硬件和驱动 为了使 LCD 正常工作,需先完成以下步骤: - 设置显示分辨率以适配 ILI9325 控制器和 LCD 屏幕规格[^1]。 - 初始化 SPI 或并口通信接口,确保 MCU 可与显示屏正常交互。 #### 安装和配置 UCGUI 图形库 安装 UCGUI 后,需要对其进行适当配置以便于使用 STM32 的内存资源。这通常包括定义颜色模式、字体以及窗口管理策略等内容。 #### 创建基本框架代码 下面是一个简单的例子展示如何利用 ucgui 构建基础菜单结构: ```c #include "uc_gui.h" // 定义按钮回调函数原型 void on_button_click(void); int main() { // 初始化 LCD 和 GUI GUI_Init(); // 创建一个窗口 WM_HWIN hWin = WM_CreateWindowAsChild(0, 0, LCD_GetXSize(), LCD_GetYSize(), WM_HBKWIN, WM_CF_SHOW | WM_CF_MEMDEV, NULL, 0); // 添加第一个按钮控件到该窗口上 WM_SetCallback(hWin, &cb_window); BUTTON_Handle hButton = BUTTON_CreateEx(10, 10, 80, 30, hWin, BM eshownormal|BM_FOCUSED, 0, ID_BUTTON_0 ); BUTTON_SetText(hButton , "Start"); BUTTON_SetUser(hButton ,(void*)on_button_click ); while (1){ GUI_Delay(100); // 循环刷新屏幕 } } // 按钮点击事件处理函数 void on_button_click(){ GUI_DispStringAt("Button Clicked!", 10, 60); } ``` 此示例展示了如何创建一个带有按钮的基础窗口,并设置了当用户按下这个按钮时触发的动作——即在屏幕上打印一条消息。 #### 使用 RTOS 提升性能 对于更复杂的项目来说,可能还需要引入实时操作系统(RTOS),如 FreeRTOS 或者 RT-Thread,这样可以帮助更好地管理和分配任务时间片[^2][^4]。例如可以通过创建独立的任务分别负责绘制 UI 组件或者响应触摸输入等操作。 #### 开发工具的选择 考虑到项目的复杂度和个人偏好,可以选择 Keil MDK 或 IAR Embedded Workbench 这样的集成开发环境来进行编程调试活动[^3]。两者各有优劣之处,但都提供了良好的支持和服务给开发者们去构建基于 ARM Cortex-M 系列处理器的应用程序。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

詩不诉卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值