Skip to content

Instantly share code, notes, and snippets.

@pyrho
Created October 10, 2024 22:26
Show Gist options
  • Save pyrho/f6ca8eea7173e274cd227174ea46bb14 to your computer and use it in GitHub Desktop.
Save pyrho/f6ca8eea7173e274cd227174ea46bb14 to your computer and use it in GitHub Desktop.
WM8960 codec init function for STM32
/*
* wm8960.c
*
* Date: 2024-09-03
* Author: @pyrho
*/
#include "wm8960.h"
#include "stm32h7xx_hal_def.h"
#include "stm32h7xx_hal_i2c.h"
static HAL_StatusTypeDef _writeRegister(I2C_HandleTypeDef *hi2c,
uint8_t address, uint16_t data);
// P.63 of datasheet
// 7bit addr + R/W bit, MSB first
// The 7bit address is `0b0011010` the 8th bit is the R/W bit which needs to
// be set to `0` to be able to write.
// So this translates to 0x34 as stated in the datasheet
// Sparkfun uses `0x1A`, because they're using Arduino's "Wire" which I guess
// does the left shift on its own.
#define WM8960_I2C_ADDR 0x34
// Private API {{{
//
static HAL_StatusTypeDef _writeRegister(I2C_HandleTypeDef *hi2c,
uint8_t address, uint16_t data) {
// Shift the register address left 1 bit to leave room to the 9th
// bit of the data
uint16_t address_byte = address << 1;
// Add the MSbit of the data to write to the register's address
address_byte |= (data >> 8);
uint8_t data_without_9th_bit = (0x00FF & data);
return HAL_I2C_Mem_Write(hi2c, WM8960_I2C_ADDR, address_byte,
I2C_MEMADD_SIZE_8BIT, &data_without_9th_bit, 1,
HAL_MAX_DELAY);
}
// }}}
// Public API {{{
bool WM8960_isReady(I2C_HandleTypeDef *hi2c) {
return HAL_I2C_IsDeviceReady(hi2c, WM8960_I2C_ADDR, 5, 100) == HAL_OK;
}
HAL_StatusTypeDef WM8960_init(I2C_HandleTypeDef *hi2c) {
/*
* #Observations
* This these clock settings, this gives a SYSCLK @ 12.285MHz (close to the
* reference 12.288)
*/
HAL_StatusTypeDef ret = 0;
// Reset
ret += _writeRegister(hi2c, 0x0f, 0b000000001);
// IPVU > 8
// LINMUTE > 7
// LIZC > 6
// LINVOL > 5:0
// This is only for LINPUT1
// ret += _writeRegister(hi2c, 0x00, 0b101111111);
// PIVU > 8
// RINMUTE > 7
// RIZC > 6
// RINVOL > 5:0
// This is only for RINPUT1
// ret += _writeRegister(hi2c, 0x01, 0b101111111);
// OUT1VU > 8 > 1
// LO1ZC > 7 > 1
// LOUT1VOL > 6:0 > 1111111 > = +6dB
ret += _writeRegister(hi2c, 0x02, 0b111111111);
ret += _writeRegister(hi2c, 0x02, 0b111111111);
// OUT1VU > 8 > 1
// RO1ZC > 7 > 1
// ROUT1VOL > 6:0 > 111111 > = +6dB
ret += _writeRegister(hi2c, 0x03, 0b111111111);
ret += _writeRegister(hi2c, 0x03, 0b111111111);
// Clocks {{{
// ADCDIV > 8:6 > 000
// DACDIV > 5:3 > 000
// SYSCLKDIV > 2:1 > 10
// CLKSEL > 0 > 1
ret += _writeRegister(hi2c, 0x04, 0b000000101);
// OPCLKDIV > 8:6 > 000 > don't care
// SDM > 5 > 1 > Fractional mode, because we need to provide the
// factional part "K" of the PLL PLLPRESCALE > 4 > 1 > /2, see table P.
// 61, values for 24MHz PLLN > 3:0 > 1000 > 8, see table P. 61, values
// for 24MHz
ret += _writeRegister(hi2c, 0x34, 0b000111000);
// 0x3126E8 => 0b 0011 0001 0110 1110 1000
// 0x3126E8 => 0b 000011 00010110 11101000
// input 24
// desired: 12.288
// prescale 2
// sysclkdiv 2
// R: 8.192
// N: 0x8
// K: 0x3126E8
//
// RES > 8 > 0
// PLLK > 7:0 >
ret += _writeRegister(hi2c, 0x35, 0x31);
// RES > 8 > 0
// PLLK > 7:0 >
ret += _writeRegister(hi2c, 0x36, 0x26);
// RES > 8 > 0
// PLLK > 7:0 >
ret += _writeRegister(hi2c, 0x37, 0xe8);
// RES > 8:7 > 00
// ALRCGPIO > 6 > 1 > For debugging the clock
// WL8 > 5 > 0
// DACCOMP > 4:3 > 00
// ADCCOMP > 2:1 > 00
// LOOPBACK > 0 > 0
ret += _writeRegister(hi2c, 0x09, 0b001000000);
// }}}
// Default clock is fine (but maybe not, since the datasheet only mentions a
// max MCLK frequency of 12.288MHz, but the onboard xtal is 25MHz) According
// to P.61 this is it. But actually if I want 48Khz, targeting 12.288 is
// better that 11.2896 So for a 24Mhz crystal, this is better:
// 24 12.288 98.304 2 2 4 8.192 8h 0x3126E8
//
// RES > 8 > 0
// DACDIV2 > 7 > 0
// ADCPOL > 6:5 > 00
// RES > 4 > 0
// DACMU > 3 > 0
// DEEMPH > 2:1 > 00
ret += _writeRegister(hi2c, 0x05, 0b000000000);
// DACVU > 8 > 1
// LDACVOL > 7:0 > 1111 1111 > 0dB
ret += _writeRegister(hi2c, 0x0a, 0b111111111);
// Doubled for VU
ret += _writeRegister(hi2c, 0x0a, 0b111111111);
// DACVU > 8 > 1
// RDACVOL > 7:0 > 1111 1111 > 0dB
ret += _writeRegister(hi2c, 0x0b, 0b111111111);
// Doubled for VU
ret += _writeRegister(hi2c, 0x0b, 0b111111111);
// RES > 8 > 0
// NGTH > 7:3 > 00000
// RES > 2:1 > 00
// NGAT > 0 > 0 > Noise gate
ret += _writeRegister(hi2c, 0x14, 0b00000001);
// ADCVU > 8 > 1
// LADCVOL > 7:0 > 1111 1111 > 0000 0000 = 0db / 1111 1111 = +30dB / 0.5 steps
// 0b11000011 = 0dB
ret += _writeRegister(hi2c, 0x15, 0b111000011);
// Doubled for VU
ret += _writeRegister(hi2c, 0x15, 0b111000011);
// ADCVU > 8 > 1
// RADCVOL > 7:0 > 1111 1111 > 0000 0000 = 0db / 1111 1111 = +30dB / 0.5 steps
// 0b11000011 = 0dB
ret += _writeRegister(hi2c, 0x16, 0b111000011);
// Doubled for VU
ret += _writeRegister(hi2c, 0x16, 0b111000011);
// VMIDSEL > 8:7 > 01
// VREF > 6 > 1
// AINL > 5 > 1
// AINR > 4 > 1
// ADCL > 3 > 1
// ADCR > 2 > 1
// MICB > 1 > 0
// DIGENB > 0 > 0
ret += _writeRegister(hi2c, 0x19, 0b011111100);
// DACL > 8 > 1
// DACR > 7 > 1
// LOUT1 > 6 > 1
// ROUT1 > 5 > 1
// SPKL > 4 > 1
// SPKR > 3 > 1
// RES > 2 > 0
// OUT3 > 1 > 1
// PLL_EN > 0 > 1
ret += _writeRegister(hi2c, 0x1a, 0b111111011);
// LMN1 > 8 > 0
// LMP3 > 7 > 0
// LMP2 > 6 > 0
// LMICBOOST > 5:4 > 00
// LMIC2B > 3 > 0
// RES > 2:0 > 000
ret += _writeRegister(hi2c, 0x20, 0b000000000);
// RMN1 > 8 > 0
// RMP3 > 7 > 0
// RMP2 > 6 > 0
// RMICBOOST > 5:4 > 00
// RMIC2B > 3 > 0
// RES > 2:0 > 000
ret += _writeRegister(hi2c, 0x21, 0b000000000);
// LD2LO > 8 > 1
// LI2LO > 7 > 0
// LI2LOVOL > 6:4 > 000
// RES > 3:0 > 0000
ret += _writeRegister(hi2c, 0x22, 0b100000000);
// RD2LO > 8 > 1
// RI2LO > 7 > 0
// RI2LOVOL > 6:4 > 000
// RES > 3:0 > 0000
ret += _writeRegister(hi2c, 0x25, 0b100000000);
// RES > 8:7 > 00
// LIN3BOOST > 6:4 > 000
// LIN2BOOST > 3:1 > 100 > 000 = mute / 001 = -12dB / 111 = +6dB (3dB steps)
// RES > 0 > 0
ret += _writeRegister(hi2c, 0x2B, 0b000001000);
// RES > 8:7 > 00
// RIN3BOOST > 6:4 > 000
// RIN2BOOST > 3:1 > 100 > 000 = mute / 001 = -12dB / 111 = +6dB (3dB steps)
// RES > 0 > 0
ret += _writeRegister(hi2c, 0x2C, 0b000001000);
// RES > 8:6 > 000
// LMIC > 5 > 0
// RMIC > 4 > 0
// LOMIX > 3 > 1
// ROMIX > 2 > 1
// RES > 1:0 > 00
ret += _writeRegister(hi2c, 0x2f, 0b000001100);
// RES > 8 > 0
// GPIOPOL > 7 > 0
// GPIOSEL > 6:4 > 100 > SYSCLK OUT
// HPSEL > 3:2 > 11
// TSENSEN > 1 > 1
// MBSEL > 0 > 0
ret += _writeRegister(hi2c, 0x30, 0b001001110);
return ret;
}
// }}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment