|
| 1 | +/* |
| 2 | + * Example to show using I2C and I2C with libopencm3. |
| 3 | + * This is intended for the STM32F411-Discovery demoboard. |
| 4 | + * It has a CS42L22 audio DAC onboard. |
| 5 | + * |
| 6 | + * The device plays a 1kHz sine through the audio jack. |
| 7 | + */ |
| 8 | + |
| 9 | + |
| 10 | +#include <libopencm3/stm32/gpio.h> |
| 11 | +#include <libopencm3/stm32/i2c.h> |
| 12 | +#include <libopencm3/stm32/rcc.h> |
| 13 | +#include <libopencm3/stm32/spi.h> |
| 14 | + |
| 15 | + |
| 16 | +/* Test audio: 8 points of a sine. |
| 17 | + * At Fs=8kHz, this means a 1kHz audio sine |
| 18 | + * (the other channel is mute) |
| 19 | + */ |
| 20 | +#define VOL 0x0020 |
| 21 | +#define D16(x) ((int16_t)(x*VOL) ) |
| 22 | +//+ 0x7fff) |
| 23 | +int16_t audio[16]= |
| 24 | +{ D16(0), 0, |
| 25 | + D16(0.70711), 0, |
| 26 | + D16(1), 0, |
| 27 | + D16(0.70711), 0, |
| 28 | + D16(0), 0, |
| 29 | + D16(-0.70711),0, |
| 30 | + D16(-0.9999), 0, |
| 31 | + D16(-0.707), 0 }; |
| 32 | + |
| 33 | + |
| 34 | +static void write_i2c_to_audiochip( uint8_t reg, uint8_t contents) |
| 35 | +{ |
| 36 | + uint8_t packet[2]; |
| 37 | + packet[0] = reg; |
| 38 | + packet[1] = contents; |
| 39 | + /* STM32F411 gives device address with R/W bit, |
| 40 | + * libopencm wants it without it */ |
| 41 | + uint8_t address = (0x94)>>1; |
| 42 | + |
| 43 | + i2c_transfer7(I2C1, address, packet, 2, NULL, 0); |
| 44 | +} |
| 45 | + |
| 46 | +int main(void) |
| 47 | +{ |
| 48 | + /* Set device clocks from opencm3 provided preset.*/ |
| 49 | + const struct rcc_clock_scale *clocks = &rcc_hsi_configs[RCC_CLOCK_3V3_84MHZ]; |
| 50 | + rcc_clock_setup_pll( clocks ); |
| 51 | + |
| 52 | + rcc_periph_clock_enable(RCC_GPIOA); |
| 53 | + rcc_periph_clock_enable(RCC_GPIOB); |
| 54 | + rcc_periph_clock_enable(RCC_GPIOC); |
| 55 | + rcc_periph_clock_enable(RCC_GPIOD); |
| 56 | + |
| 57 | + /* Initialize "heartbeat" LED GPIO */ |
| 58 | + gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13); |
| 59 | + |
| 60 | + /* I2C GPIO pins |
| 61 | + * PB6 - SCL (I2C clock) |
| 62 | + * PB9 - SDA (I2C data) |
| 63 | + * The board does not have pullups on the I2C lines, so |
| 64 | + * we use the chip internal pullups. |
| 65 | + * Also the pins must be open drain, as per I2C specification. |
| 66 | + * STM32F411 datasheet "Alternate Functions table" tells that |
| 67 | + * I2C is AlternateFucntion 4 for both pins. |
| 68 | + */ |
| 69 | + gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO6); |
| 70 | + gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO9); |
| 71 | + gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO6); |
| 72 | + gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO9); |
| 73 | + gpio_set_af(GPIOB, GPIO_AF4, GPIO6); |
| 74 | + gpio_set_af(GPIOB, GPIO_AF4, GPIO9); |
| 75 | + |
| 76 | + /* Initialize the I2C itself. |
| 77 | + * Since we are master, we would not need to initialize slave |
| 78 | + * address, but this is the only libopencm3 API call that sets |
| 79 | + * the 'bit14 of CCR' - a bit in the I2C that is HW reset to 0, |
| 80 | + * but manual says 'must be 1' */ |
| 81 | + rcc_periph_clock_enable(RCC_I2C1); |
| 82 | + i2c_peripheral_disable(I2C1); |
| 83 | + i2c_set_speed(I2C1, i2c_speed_sm_100k, clocks->apb1_frequency/1000000); |
| 84 | + i2c_set_own_7bit_slave_address(I2C1, 0); |
| 85 | + i2c_peripheral_enable(I2C1); |
| 86 | + |
| 87 | + /* Initialize I2S. |
| 88 | + * I2S is implemented as a HW mode of the SPI peripheral. |
| 89 | + * Since this is a STM32F411, there is a separate I2S PLL |
| 90 | + * that needs to be enabled. |
| 91 | + */ |
| 92 | + rcc_osc_on(RCC_PLLI2S); |
| 93 | + rcc_periph_clock_enable(RCC_SPI3); |
| 94 | + i2s_disable(SPI3); |
| 95 | + i2s_set_standard(SPI3, i2s_standard_philips); |
| 96 | + i2s_set_dataformat(SPI3, i2s_dataframe_ch16_data16); |
| 97 | + i2s_set_mode(SPI3, i2s_mode_master_transmit); |
| 98 | + i2s_masterclock_enable(SPI3); |
| 99 | + /* RCC_PLLI2SCFGR configured values are: |
| 100 | + * 0x24003010 i.e. |
| 101 | + * PLLR = 2 |
| 102 | + * PLLI2SN = 192 |
| 103 | + * PLLI2SM = 16 |
| 104 | + * And since the input is PLL source (i.e. HSI = 16MHz) |
| 105 | + * The I2S clock = 16 / 16 * 192 / 2 = 96MHz |
| 106 | + * Calculate sampling frequency from equation given in |
| 107 | + * STM32F411 reference manual: |
| 108 | + * Fs = I2Sclk/ (32*2 * ((2*I2SDIV)+ODD)*4) |
| 109 | + * I2SDIV = I2Sclk/(512*Fs) |
| 110 | + * I2SDIV=24 => 23,4 so 23 + ODD bit set |
| 111 | + */ |
| 112 | + i2s_set_clockdiv(SPI3, 23, 1); |
| 113 | + i2s_enable(SPI3); |
| 114 | + |
| 115 | + |
| 116 | + /* I2S pins: |
| 117 | + * Master clock: PC7 |
| 118 | + * Bit clock: PC10 |
| 119 | + * Data: PC12 |
| 120 | + * L/R clock: PA4 |
| 121 | + */ |
| 122 | + gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO7); |
| 123 | + gpio_set_af(GPIOC, GPIO_AF6, GPIO7); |
| 124 | + gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO10); |
| 125 | + gpio_set_af(GPIOC, GPIO_AF6, GPIO10); |
| 126 | + gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO12); |
| 127 | + gpio_set_af(GPIOC, GPIO_AF6, GPIO12); |
| 128 | + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO4); |
| 129 | + gpio_set_af(GPIOA, GPIO_AF6, GPIO4); |
| 130 | + |
| 131 | + |
| 132 | + /* Initialize the Audio DAC, as per its datasheet. |
| 133 | + * CS43L22 /RESET is connected to PD4, first release it. Then write |
| 134 | + * minimum set of needed settings. */ |
| 135 | + gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); |
| 136 | + gpio_set(GPIOD, GPIO4); |
| 137 | + write_i2c_to_audiochip(0x06, 0x04); // interface control 1: set I2S dataformat |
| 138 | + write_i2c_to_audiochip(0x02, 0x9e); // power control 1: Magic value to power up the chip |
| 139 | + |
| 140 | + |
| 141 | + while(1) { |
| 142 | + /* Blink the heartbeat LED */ |
| 143 | + static int blinkslowdown=0; |
| 144 | + if( ++blinkslowdown == 8000) { |
| 145 | + gpio_toggle(GPIOD, GPIO13); |
| 146 | + blinkslowdown=0; |
| 147 | + } |
| 148 | + |
| 149 | + for( unsigned i=0; i < sizeof(audio)/sizeof(audio[0]); i++) |
| 150 | + spi_send(SPI3, audio[i]); |
| 151 | + } |
| 152 | + return 0; |
| 153 | +} |
| 154 | + |
0 commit comments