Created
October 10, 2024 22:26
-
-
Save pyrho/f6ca8eea7173e274cd227174ea46bb14 to your computer and use it in GitHub Desktop.
WM8960 codec init function for STM32
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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