STMicroelectronics' implementation of an I2S driver, also including DMA support.

Dependents:   temp X_NUCLEO_CCA01M1 X_NUCLEO_CCA01M1 X_NUCLEO_CCA02M1

Platform compatibility

This driver has been designed to support a wide range of the Nucleo F4 Family of platforms and MCUs, but not all members of this family support I2S and/or some of the members might require slight modifications to the sources of this driver in order to make it work on those.

This driver has for now been tested only with the following platforms:

Committer:
Davide Aliprandi
Date:
Wed Dec 21 20:24:16 2016 +0100
Revision:
4:21603d68bcf7
Parent:
0:752e74bf5ef1
Child:
9:c4c2240e06d6
Added algorithm to automatically harmonize two I2S peripherals' frequencies.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Wolfgang Betz 0:752e74bf5ef1 1 #include "drivers/I2S.h"
Wolfgang Betz 0:752e74bf5ef1 2 #include "platform/critical.h"
Wolfgang Betz 0:752e74bf5ef1 3 #include "platform/mbed_assert.h"
Wolfgang Betz 0:752e74bf5ef1 4
Wolfgang Betz 0:752e74bf5ef1 5 #if DEVICE_I2S
Wolfgang Betz 0:752e74bf5ef1 6
Wolfgang Betz 0:752e74bf5ef1 7 namespace mbed {
Wolfgang Betz 0:752e74bf5ef1 8
Wolfgang Betz 0:752e74bf5ef1 9 /* betzw - WAS
Wolfgang Betz 0:752e74bf5ef1 10 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 11 CircularBuffer<Transaction<I2S>, TRANSACTION_QUEUE_SIZE_I2S> I2S::_transaction_buffer;
Wolfgang Betz 0:752e74bf5ef1 12 #endif
Wolfgang Betz 0:752e74bf5ef1 13 */
Wolfgang Betz 0:752e74bf5ef1 14
Wolfgang Betz 0:752e74bf5ef1 15 I2S* I2S::_owner = NULL;
Wolfgang Betz 0:752e74bf5ef1 16 SingletonPtr<PlatformMutex> I2S::_mutex; // intentional class level lock!
Wolfgang Betz 0:752e74bf5ef1 17
Wolfgang Betz 0:752e74bf5ef1 18 rtos::Thread I2S::I2sBhHandler::_i2s_bh_daemon;
Wolfgang Betz 0:752e74bf5ef1 19 events::EventQueue I2S::I2sBhHandler::_i2s_bh_queue;
Wolfgang Betz 0:752e74bf5ef1 20
Wolfgang Betz 0:752e74bf5ef1 21 void I2S::lock() {
Wolfgang Betz 0:752e74bf5ef1 22 #ifdef NDEBUG
Wolfgang Betz 0:752e74bf5ef1 23 _mutex->lock(); // intentional class level lock!
Wolfgang Betz 0:752e74bf5ef1 24 #else
Wolfgang Betz 0:752e74bf5ef1 25 osStatus ret = _mutex->lock(); // intentional class level lock!
Wolfgang Betz 0:752e74bf5ef1 26 MBED_ASSERT(ret == osOK);
Wolfgang Betz 0:752e74bf5ef1 27 #endif
Wolfgang Betz 0:752e74bf5ef1 28 }
Wolfgang Betz 0:752e74bf5ef1 29
Wolfgang Betz 0:752e74bf5ef1 30 void I2S::unlock() {
Wolfgang Betz 0:752e74bf5ef1 31 #ifdef NDEBUG
Wolfgang Betz 0:752e74bf5ef1 32 _mutex->unlock(); // intentional class level lock!
Wolfgang Betz 0:752e74bf5ef1 33 #else
Wolfgang Betz 0:752e74bf5ef1 34 osStatus ret = _mutex->unlock(); // intentional class level lock!
Wolfgang Betz 0:752e74bf5ef1 35 MBED_ASSERT(ret == osOK);
Wolfgang Betz 0:752e74bf5ef1 36 #endif
Wolfgang Betz 0:752e74bf5ef1 37 }
Wolfgang Betz 0:752e74bf5ef1 38
Wolfgang Betz 0:752e74bf5ef1 39 I2S::I2S(PinName dpin, PinName clk, PinName wsel, PinName fdpin, PinName mck) :
Wolfgang Betz 0:752e74bf5ef1 40 _i2s(),
Wolfgang Betz 0:752e74bf5ef1 41 _irq_tx(this), _irq_rx(this),
Wolfgang Betz 0:752e74bf5ef1 42 _priority(MEDIUM),
Wolfgang Betz 0:752e74bf5ef1 43 _dbits(16),
Wolfgang Betz 0:752e74bf5ef1 44 _fbits(16),
Wolfgang Betz 0:752e74bf5ef1 45 _polarity(0),
Wolfgang Betz 0:752e74bf5ef1 46 _protocol(PHILIPS),
Wolfgang Betz 0:752e74bf5ef1 47 _mode(MASTER_TX),
Wolfgang Betz 0:752e74bf5ef1 48 _circular(false),
Wolfgang Betz 0:752e74bf5ef1 49 _hz(44100) {
Wolfgang Betz 0:752e74bf5ef1 50 lock();
Wolfgang Betz 0:752e74bf5ef1 51 /* Init instance */
Wolfgang Betz 0:752e74bf5ef1 52 i2s_init(&_i2s, dpin, clk, wsel, fdpin, mck, _mode);
Wolfgang Betz 0:752e74bf5ef1 53 acquire();
Wolfgang Betz 0:752e74bf5ef1 54 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 55 /* Init bottom half daemon */
Wolfgang Betz 0:752e74bf5ef1 56 I2sBhHandler::init();
Wolfgang Betz 0:752e74bf5ef1 57 #endif
Wolfgang Betz 0:752e74bf5ef1 58 unlock();
Wolfgang Betz 0:752e74bf5ef1 59 }
Wolfgang Betz 0:752e74bf5ef1 60
Wolfgang Betz 0:752e74bf5ef1 61 int I2S::format(int dbits, int fbits, int polarity) {
Wolfgang Betz 0:752e74bf5ef1 62 lock();
Wolfgang Betz 0:752e74bf5ef1 63 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 64 unlock();
Wolfgang Betz 0:752e74bf5ef1 65 return -1;
Wolfgang Betz 0:752e74bf5ef1 66 }
Wolfgang Betz 0:752e74bf5ef1 67 _dbits = dbits;
Wolfgang Betz 0:752e74bf5ef1 68 _fbits = fbits;
Wolfgang Betz 0:752e74bf5ef1 69 _polarity = polarity;
Wolfgang Betz 0:752e74bf5ef1 70 I2S::_owner = NULL; // Not that elegant, but works. rmeyer
Wolfgang Betz 0:752e74bf5ef1 71 acquire();
Wolfgang Betz 0:752e74bf5ef1 72 unlock();
Wolfgang Betz 0:752e74bf5ef1 73 return 0;
Wolfgang Betz 0:752e74bf5ef1 74 }
Wolfgang Betz 0:752e74bf5ef1 75
Wolfgang Betz 0:752e74bf5ef1 76 int I2S::audio_frequency(unsigned int hz) {
Wolfgang Betz 0:752e74bf5ef1 77 lock();
Wolfgang Betz 0:752e74bf5ef1 78 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 79 unlock();
Wolfgang Betz 0:752e74bf5ef1 80 return -1;
Wolfgang Betz 0:752e74bf5ef1 81 }
Wolfgang Betz 0:752e74bf5ef1 82 _hz = hz;
Wolfgang Betz 0:752e74bf5ef1 83 I2S::_owner = NULL; // Not that elegant, but works. rmeyer
Wolfgang Betz 0:752e74bf5ef1 84 acquire();
Wolfgang Betz 0:752e74bf5ef1 85 unlock();
Wolfgang Betz 0:752e74bf5ef1 86 return 0;
Wolfgang Betz 0:752e74bf5ef1 87 }
Wolfgang Betz 0:752e74bf5ef1 88
Wolfgang Betz 0:752e74bf5ef1 89 int I2S::set_protocol(i2s_bitorder_t protocol) {
Wolfgang Betz 0:752e74bf5ef1 90 lock();
Wolfgang Betz 0:752e74bf5ef1 91 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 92 unlock();
Wolfgang Betz 0:752e74bf5ef1 93 return -1;
Wolfgang Betz 0:752e74bf5ef1 94 }
Wolfgang Betz 0:752e74bf5ef1 95 _protocol = protocol;
Wolfgang Betz 0:752e74bf5ef1 96 I2S::_owner = NULL; // Not that elegant, but works. rmeyer
Wolfgang Betz 0:752e74bf5ef1 97 acquire();
Wolfgang Betz 0:752e74bf5ef1 98 unlock();
Wolfgang Betz 0:752e74bf5ef1 99 return 0;
Wolfgang Betz 0:752e74bf5ef1 100 }
Wolfgang Betz 0:752e74bf5ef1 101
Wolfgang Betz 0:752e74bf5ef1 102 int I2S::set_mode(i2s_mode_t mode, bool circular) {
Wolfgang Betz 0:752e74bf5ef1 103 lock();
Wolfgang Betz 0:752e74bf5ef1 104 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 105 unlock();
Wolfgang Betz 0:752e74bf5ef1 106 return -1;
Wolfgang Betz 0:752e74bf5ef1 107 }
Wolfgang Betz 0:752e74bf5ef1 108 _mode = mode;
Wolfgang Betz 0:752e74bf5ef1 109 _circular = circular;
Wolfgang Betz 0:752e74bf5ef1 110 I2S::_owner = NULL; // Not that elegant, but works. rmeyer
Wolfgang Betz 0:752e74bf5ef1 111 acquire();
Wolfgang Betz 0:752e74bf5ef1 112 unlock();
Wolfgang Betz 0:752e74bf5ef1 113 return 0;
Wolfgang Betz 0:752e74bf5ef1 114 }
Wolfgang Betz 0:752e74bf5ef1 115
Wolfgang Betz 0:752e74bf5ef1 116 void I2S::abort_transfer()
Wolfgang Betz 0:752e74bf5ef1 117 {
Wolfgang Betz 0:752e74bf5ef1 118 lock();
Wolfgang Betz 0:752e74bf5ef1 119 i2s_abort_asynch(&_i2s);
Wolfgang Betz 0:752e74bf5ef1 120 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 121 dequeue_transaction();
Wolfgang Betz 0:752e74bf5ef1 122 #endif
Wolfgang Betz 0:752e74bf5ef1 123 unlock();
Wolfgang Betz 0:752e74bf5ef1 124 }
Wolfgang Betz 0:752e74bf5ef1 125
Wolfgang Betz 0:752e74bf5ef1 126
Wolfgang Betz 0:752e74bf5ef1 127 void I2S::clear_transfer_buffer()
Wolfgang Betz 0:752e74bf5ef1 128 {
Wolfgang Betz 0:752e74bf5ef1 129 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 130 lock();
Wolfgang Betz 0:752e74bf5ef1 131 _transaction_buffer.reset();
Wolfgang Betz 0:752e74bf5ef1 132 unlock();
Wolfgang Betz 0:752e74bf5ef1 133 #endif
Wolfgang Betz 0:752e74bf5ef1 134 }
Wolfgang Betz 0:752e74bf5ef1 135
Wolfgang Betz 0:752e74bf5ef1 136 void I2S::abort_all_transfers()
Wolfgang Betz 0:752e74bf5ef1 137 {
Wolfgang Betz 0:752e74bf5ef1 138 lock();
Wolfgang Betz 0:752e74bf5ef1 139 clear_transfer_buffer();
Wolfgang Betz 0:752e74bf5ef1 140 abort_transfer();
Wolfgang Betz 0:752e74bf5ef1 141 unlock();
Wolfgang Betz 0:752e74bf5ef1 142 }
Wolfgang Betz 0:752e74bf5ef1 143
Wolfgang Betz 0:752e74bf5ef1 144 int I2S::get_transfer_status()
Wolfgang Betz 0:752e74bf5ef1 145 {
Wolfgang Betz 0:752e74bf5ef1 146 lock();
Wolfgang Betz 0:752e74bf5ef1 147 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 148 unlock();
Wolfgang Betz 0:752e74bf5ef1 149 return -1;
Wolfgang Betz 0:752e74bf5ef1 150 }
Wolfgang Betz 0:752e74bf5ef1 151 unlock();
Wolfgang Betz 0:752e74bf5ef1 152 return 0;
Wolfgang Betz 0:752e74bf5ef1 153 }
Wolfgang Betz 0:752e74bf5ef1 154
Wolfgang Betz 0:752e74bf5ef1 155 unsigned int I2S::get_module()
Wolfgang Betz 0:752e74bf5ef1 156 {
Wolfgang Betz 0:752e74bf5ef1 157 return i2s_get_module(&_i2s);
Wolfgang Betz 0:752e74bf5ef1 158 }
Wolfgang Betz 0:752e74bf5ef1 159
Wolfgang Betz 0:752e74bf5ef1 160 int I2S::set_dma_priority(i2s_dma_prio_t prio)
Wolfgang Betz 0:752e74bf5ef1 161 {
Wolfgang Betz 0:752e74bf5ef1 162 lock();
Wolfgang Betz 0:752e74bf5ef1 163 if (i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 164 unlock();
Wolfgang Betz 0:752e74bf5ef1 165 return -1;
Wolfgang Betz 0:752e74bf5ef1 166 }
Wolfgang Betz 0:752e74bf5ef1 167 _priority = prio;
Wolfgang Betz 0:752e74bf5ef1 168 unlock();
Wolfgang Betz 0:752e74bf5ef1 169 return 0;
Wolfgang Betz 0:752e74bf5ef1 170 }
Wolfgang Betz 0:752e74bf5ef1 171
Wolfgang Betz 0:752e74bf5ef1 172 int I2S::queue_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length, const event_callback_t& callback, int event)
Wolfgang Betz 0:752e74bf5ef1 173 { // betzw: MUST be called with lock held!
Wolfgang Betz 0:752e74bf5ef1 174 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 175 transaction_t t;
Wolfgang Betz 0:752e74bf5ef1 176
Wolfgang Betz 0:752e74bf5ef1 177 t.tx_buffer = const_cast<void *>(tx_buffer);
Wolfgang Betz 0:752e74bf5ef1 178 t.tx_length = tx_length;
Wolfgang Betz 0:752e74bf5ef1 179 t.rx_buffer = rx_buffer;
Wolfgang Betz 0:752e74bf5ef1 180 t.rx_length = rx_length;
Wolfgang Betz 0:752e74bf5ef1 181 t.event = event;
Wolfgang Betz 0:752e74bf5ef1 182 t.callback = callback;
Wolfgang Betz 0:752e74bf5ef1 183 t.width = 16;
Wolfgang Betz 0:752e74bf5ef1 184 Transaction<I2S> transaction(this, t);
Wolfgang Betz 0:752e74bf5ef1 185 core_util_critical_section_enter();
Wolfgang Betz 0:752e74bf5ef1 186 if (_transaction_buffer.full()) {
Wolfgang Betz 0:752e74bf5ef1 187 core_util_critical_section_enter();
Wolfgang Betz 0:752e74bf5ef1 188 return -1; // the buffer is full
Wolfgang Betz 0:752e74bf5ef1 189 } else {
Wolfgang Betz 0:752e74bf5ef1 190 _transaction_buffer.push(transaction);
Wolfgang Betz 0:752e74bf5ef1 191 core_util_critical_section_exit();
Wolfgang Betz 0:752e74bf5ef1 192 // betzw - seems to be redundant - WAS: dequeue_transaction();
Wolfgang Betz 0:752e74bf5ef1 193 return 0;
Wolfgang Betz 0:752e74bf5ef1 194 }
Wolfgang Betz 0:752e74bf5ef1 195 #else
Wolfgang Betz 0:752e74bf5ef1 196 return -1;
Wolfgang Betz 0:752e74bf5ef1 197 #endif
Wolfgang Betz 0:752e74bf5ef1 198 }
Wolfgang Betz 0:752e74bf5ef1 199
Wolfgang Betz 0:752e74bf5ef1 200
Wolfgang Betz 0:752e74bf5ef1 201 // ignore the fact there are multiple physical i2s's, and always update if it wasn't us last
Wolfgang Betz 0:752e74bf5ef1 202 void I2S::acquire() { // betzw: MUST be called with lock held!
Wolfgang Betz 0:752e74bf5ef1 203 if (_owner != this) {
Wolfgang Betz 0:752e74bf5ef1 204 i2s_format(&_i2s, _dbits, _fbits, _polarity);
Wolfgang Betz 0:752e74bf5ef1 205 i2s_audio_frequency(&_i2s, _hz);
Wolfgang Betz 0:752e74bf5ef1 206 i2s_set_protocol(&_i2s, _protocol);
Wolfgang Betz 0:752e74bf5ef1 207 i2s_set_mode(&_i2s, _mode);
Wolfgang Betz 0:752e74bf5ef1 208 _owner = this;
Wolfgang Betz 0:752e74bf5ef1 209 }
Wolfgang Betz 0:752e74bf5ef1 210 }
Wolfgang Betz 0:752e74bf5ef1 211
Wolfgang Betz 0:752e74bf5ef1 212 void I2S::start_transfer(const void *tx_buffer, int tx_length, void *rx_buffer, int rx_length,
Wolfgang Betz 0:752e74bf5ef1 213 const event_callback_t& callback, int event)
Wolfgang Betz 0:752e74bf5ef1 214 { // betzw: MUST be called with lock held!
Wolfgang Betz 0:752e74bf5ef1 215 acquire();
Wolfgang Betz 0:752e74bf5ef1 216 _callback = callback;
Wolfgang Betz 0:752e74bf5ef1 217 _irq_tx.callback(&I2S::irq_handler_asynch_tx);
Wolfgang Betz 0:752e74bf5ef1 218 _irq_rx.callback(&I2S::irq_handler_asynch_rx);
Wolfgang Betz 0:752e74bf5ef1 219 i2s_transfer(&_i2s,
Wolfgang Betz 0:752e74bf5ef1 220 const_cast<void *>(tx_buffer), tx_length, rx_buffer, rx_length,
Wolfgang Betz 0:752e74bf5ef1 221 _circular, _priority,
Wolfgang Betz 0:752e74bf5ef1 222 _irq_tx.entry(), _irq_rx.entry(),
Wolfgang Betz 0:752e74bf5ef1 223 event);
Wolfgang Betz 0:752e74bf5ef1 224 }
Wolfgang Betz 0:752e74bf5ef1 225
Wolfgang Betz 0:752e74bf5ef1 226 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 227
Wolfgang Betz 0:752e74bf5ef1 228 void I2S::start_transaction(transaction_t *data)
Wolfgang Betz 0:752e74bf5ef1 229 { // betzw: MUST be called with lock held!
Wolfgang Betz 0:752e74bf5ef1 230 start_transfer(data->tx_buffer, data->tx_length, data->rx_buffer, data->rx_length, data->callback, data->event);
Wolfgang Betz 0:752e74bf5ef1 231 }
Wolfgang Betz 0:752e74bf5ef1 232
Wolfgang Betz 0:752e74bf5ef1 233 void I2S::dequeue_transaction()
Wolfgang Betz 0:752e74bf5ef1 234 {
Wolfgang Betz 0:752e74bf5ef1 235 lock();
Wolfgang Betz 0:752e74bf5ef1 236 if (!i2s_active(&_i2s)) {
Wolfgang Betz 0:752e74bf5ef1 237 Transaction<I2S> t;
Wolfgang Betz 0:752e74bf5ef1 238 if (_transaction_buffer.pop(t)) {
Wolfgang Betz 0:752e74bf5ef1 239 I2S* obj = t.get_object();
Wolfgang Betz 0:752e74bf5ef1 240 transaction_t* data = t.get_transaction();
Wolfgang Betz 0:752e74bf5ef1 241 MBED_ASSERT(obj == this); // betzw: what if 'obj' is NOT equal to 'this'?
Wolfgang Betz 0:752e74bf5ef1 242 obj->start_transaction(data);
Wolfgang Betz 0:752e74bf5ef1 243 }
Wolfgang Betz 0:752e74bf5ef1 244 }
Wolfgang Betz 0:752e74bf5ef1 245 unlock();
Wolfgang Betz 0:752e74bf5ef1 246 }
Wolfgang Betz 0:752e74bf5ef1 247
Wolfgang Betz 0:752e74bf5ef1 248 #endif
Wolfgang Betz 0:752e74bf5ef1 249
Wolfgang Betz 0:752e74bf5ef1 250 void I2S::irq_handler_asynch_rx(void)
Wolfgang Betz 0:752e74bf5ef1 251 {
Wolfgang Betz 0:752e74bf5ef1 252 int event = i2s_irq_handler_asynch(&_i2s, I2S_RX_EVENT);
Wolfgang Betz 0:752e74bf5ef1 253 if (_callback && (event & I2S_EVENT_ALL)) {
Wolfgang Betz 0:752e74bf5ef1 254 I2sBhHandler::i2s_defer_function(_callback, event & I2S_EVENT_ALL);
Wolfgang Betz 0:752e74bf5ef1 255 }
Wolfgang Betz 0:752e74bf5ef1 256 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 257 if (event & I2S_EVENT_INTERNAL_TRANSFER_COMPLETE) {
Wolfgang Betz 0:752e74bf5ef1 258 I2sBhHandler::i2s_defer_function(Callback<void()>(this, &I2S::dequeue_transaction));
Wolfgang Betz 0:752e74bf5ef1 259 }
Wolfgang Betz 0:752e74bf5ef1 260 #endif
Wolfgang Betz 0:752e74bf5ef1 261 }
Wolfgang Betz 0:752e74bf5ef1 262
Wolfgang Betz 0:752e74bf5ef1 263 void I2S::irq_handler_asynch_tx(void)
Wolfgang Betz 0:752e74bf5ef1 264 {
Wolfgang Betz 0:752e74bf5ef1 265 int event = i2s_irq_handler_asynch(&_i2s, I2S_TX_EVENT);
Wolfgang Betz 0:752e74bf5ef1 266 if (_callback && (event & I2S_EVENT_ALL)) {
Wolfgang Betz 0:752e74bf5ef1 267 I2sBhHandler::i2s_defer_function(_callback, event & I2S_EVENT_ALL);
Wolfgang Betz 0:752e74bf5ef1 268 }
Wolfgang Betz 0:752e74bf5ef1 269 #if TRANSACTION_QUEUE_SIZE_I2S
Wolfgang Betz 0:752e74bf5ef1 270 if (event & I2S_EVENT_INTERNAL_TRANSFER_COMPLETE) {
Wolfgang Betz 0:752e74bf5ef1 271 I2sBhHandler::i2s_defer_function(Callback<void()>(this, &I2S::dequeue_transaction));
Wolfgang Betz 0:752e74bf5ef1 272 }
Wolfgang Betz 0:752e74bf5ef1 273 #endif
Wolfgang Betz 0:752e74bf5ef1 274 }
Wolfgang Betz 0:752e74bf5ef1 275
Davide Aliprandi 4:21603d68bcf7 276 float I2S::compute_real_frequency(I2S *dev_i2s)
Davide Aliprandi 4:21603d68bcf7 277 {
Davide Aliprandi 4:21603d68bcf7 278 uint32_t i2sclk = 0U, i2sdiv = 2U, i2sodd = 0U, packetlength = 1U, tmp = 0U;
Davide Aliprandi 4:21603d68bcf7 279 I2S_HandleTypeDef *hi2s;
Davide Aliprandi 4:21603d68bcf7 280
Davide Aliprandi 4:21603d68bcf7 281 /* Get the I2S handle. */
Davide Aliprandi 4:21603d68bcf7 282 hi2s = i2s_get_handle(&(dev_i2s->_i2s));
Davide Aliprandi 4:21603d68bcf7 283
Davide Aliprandi 4:21603d68bcf7 284 /* Check the frame length (For the Prescaler computing). */
Davide Aliprandi 4:21603d68bcf7 285 if (hi2s->Init.DataFormat != I2S_DATAFORMAT_16B)
Davide Aliprandi 4:21603d68bcf7 286 {
Davide Aliprandi 4:21603d68bcf7 287 /* Packet length is 32 bits */
Davide Aliprandi 4:21603d68bcf7 288 packetlength = 2U;
Davide Aliprandi 4:21603d68bcf7 289 }
Davide Aliprandi 4:21603d68bcf7 290
Davide Aliprandi 4:21603d68bcf7 291 /* Get I2S source Clock frequency. */
Davide Aliprandi 4:21603d68bcf7 292 i2sclk = I2S_GetInputClock(hi2s);
Davide Aliprandi 4:21603d68bcf7 293
Davide Aliprandi 4:21603d68bcf7 294 /* Compute the Real divider depending on the MCLK output state, with a floating point. */
Davide Aliprandi 4:21603d68bcf7 295 if (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE)
Davide Aliprandi 4:21603d68bcf7 296 {
Davide Aliprandi 4:21603d68bcf7 297 /* MCLK output is enabled. */
Davide Aliprandi 4:21603d68bcf7 298 tmp = (uint32_t)(((((i2sclk / 256U) * 10U) / hi2s->Init.AudioFreq)) + 5U);
Davide Aliprandi 4:21603d68bcf7 299 }
Davide Aliprandi 4:21603d68bcf7 300 else
Davide Aliprandi 4:21603d68bcf7 301 {
Davide Aliprandi 4:21603d68bcf7 302 /* MCLK output is disabled. */
Davide Aliprandi 4:21603d68bcf7 303 tmp = (uint32_t)(((((i2sclk / (32U * packetlength)) * 10U) / hi2s->Init.AudioFreq)) + 5U);
Davide Aliprandi 4:21603d68bcf7 304 }
Davide Aliprandi 4:21603d68bcf7 305
Davide Aliprandi 4:21603d68bcf7 306 /* Remove the flatting point. */
Davide Aliprandi 4:21603d68bcf7 307 tmp = tmp / 10U;
Davide Aliprandi 4:21603d68bcf7 308
Davide Aliprandi 4:21603d68bcf7 309 /* Check the parity of the divider. */
Davide Aliprandi 4:21603d68bcf7 310 i2sodd = (uint32_t)(tmp & (uint32_t)1U);
Davide Aliprandi 4:21603d68bcf7 311
Davide Aliprandi 4:21603d68bcf7 312 /* Compute the i2sdiv prescaler. */
Davide Aliprandi 4:21603d68bcf7 313 i2sdiv = (uint32_t)((tmp - i2sodd) / 2U);
Davide Aliprandi 4:21603d68bcf7 314
Davide Aliprandi 4:21603d68bcf7 315 /* Test if the divider is 1 or 0 or greater than 0xFF. */
Davide Aliprandi 4:21603d68bcf7 316 if ((i2sdiv < 2U) || (i2sdiv > 0xFFU))
Davide Aliprandi 4:21603d68bcf7 317 {
Davide Aliprandi 4:21603d68bcf7 318 /* Set the default values. */
Davide Aliprandi 4:21603d68bcf7 319 i2sdiv = 2U;
Davide Aliprandi 4:21603d68bcf7 320 i2sodd = 0U;
Davide Aliprandi 4:21603d68bcf7 321 }
Davide Aliprandi 4:21603d68bcf7 322
Davide Aliprandi 4:21603d68bcf7 323 /* Compute the I2S frequencies. */
Davide Aliprandi 4:21603d68bcf7 324 uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32);
Davide Aliprandi 4:21603d68bcf7 325 uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1);
Davide Aliprandi 4:21603d68bcf7 326 float f = i2sclk / (2 * format_factor * ((2 * i2sdiv) + i2sodd) * mclk_factor);
Davide Aliprandi 4:21603d68bcf7 327
Davide Aliprandi 4:21603d68bcf7 328 return f;
Davide Aliprandi 4:21603d68bcf7 329 }
Davide Aliprandi 4:21603d68bcf7 330
Davide Aliprandi 4:21603d68bcf7 331 /** Computes the two-div-plus-odd factor of a given I2S objects
Davide Aliprandi 4:21603d68bcf7 332 * on a desired frequency.
Davide Aliprandi 4:21603d68bcf7 333 *
Davide Aliprandi 4:21603d68bcf7 334 * @param dev_i2s reference to the I2S object.
Davide Aliprandi 4:21603d68bcf7 335 * @frequency the desired frequency.
Davide Aliprandi 4:21603d68bcf7 336 * @return the computed two-div-plus-odd factor.
Davide Aliprandi 4:21603d68bcf7 337 */
Davide Aliprandi 4:21603d68bcf7 338 float I2S::compute_magic_factor(I2S *dev_i2s, float f)
Davide Aliprandi 4:21603d68bcf7 339 {
Davide Aliprandi 4:21603d68bcf7 340 uint32_t i2sclk = 0U, i2sdiv = 2U, i2sodd = 0U, packetlength = 1U, tmp = 0U;
Davide Aliprandi 4:21603d68bcf7 341 I2S_HandleTypeDef *hi2s;
Davide Aliprandi 4:21603d68bcf7 342
Davide Aliprandi 4:21603d68bcf7 343 /* Get the I2S handle. */
Davide Aliprandi 4:21603d68bcf7 344 hi2s = i2s_get_handle(&(dev_i2s->_i2s));
Davide Aliprandi 4:21603d68bcf7 345
Davide Aliprandi 4:21603d68bcf7 346 /* Get I2S source Clock frequency. */
Davide Aliprandi 4:21603d68bcf7 347 i2sclk = I2S_GetInputClock(hi2s);
Davide Aliprandi 4:21603d68bcf7 348 /* Compute the I2S frequencies. */
Davide Aliprandi 4:21603d68bcf7 349 uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32);
Davide Aliprandi 4:21603d68bcf7 350 uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1);
Davide Aliprandi 4:21603d68bcf7 351 float mf = i2sclk / (2 * format_factor * f * mclk_factor);
Davide Aliprandi 4:21603d68bcf7 352
Davide Aliprandi 4:21603d68bcf7 353 return mf;
Davide Aliprandi 4:21603d68bcf7 354 }
Davide Aliprandi 4:21603d68bcf7 355
Davide Aliprandi 4:21603d68bcf7 356 float I2S::compute_desired_frequency(I2S *dev_i2s, float mf)
Davide Aliprandi 4:21603d68bcf7 357 {
Davide Aliprandi 4:21603d68bcf7 358 uint32_t i2sclk = 0U, i2sdiv = 2U, i2sodd = 0U, packetlength = 1U, tmp = 0U;
Davide Aliprandi 4:21603d68bcf7 359 I2S_HandleTypeDef *hi2s;
Davide Aliprandi 4:21603d68bcf7 360
Davide Aliprandi 4:21603d68bcf7 361 /* Get the I2S handle. */
Davide Aliprandi 4:21603d68bcf7 362 hi2s = i2s_get_handle(&(dev_i2s->_i2s));
Davide Aliprandi 4:21603d68bcf7 363
Davide Aliprandi 4:21603d68bcf7 364 /* Get I2S source Clock frequency. */
Davide Aliprandi 4:21603d68bcf7 365 i2sclk = I2S_GetInputClock(hi2s);
Davide Aliprandi 4:21603d68bcf7 366
Davide Aliprandi 4:21603d68bcf7 367 /* Compute the I2S frequencies. */
Davide Aliprandi 4:21603d68bcf7 368 uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32);
Davide Aliprandi 4:21603d68bcf7 369 uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1);
Davide Aliprandi 4:21603d68bcf7 370 float f = i2sclk / (2 * format_factor * mf * mclk_factor);
Davide Aliprandi 4:21603d68bcf7 371
Davide Aliprandi 4:21603d68bcf7 372 return f;
Davide Aliprandi 4:21603d68bcf7 373 }
Davide Aliprandi 4:21603d68bcf7 374
Davide Aliprandi 4:21603d68bcf7 375 int I2S::harmonize(I2S *dev_i2s_1, I2S *dev_i2s_2)
Davide Aliprandi 4:21603d68bcf7 376 {
Davide Aliprandi 4:21603d68bcf7 377 if (dev_i2s_1->_hz == dev_i2s_2->_hz)
Davide Aliprandi 4:21603d68bcf7 378 return 0;
Davide Aliprandi 4:21603d68bcf7 379
Davide Aliprandi 4:21603d68bcf7 380 /* Compute the real frequencies. */
Davide Aliprandi 4:21603d68bcf7 381 float f1 = compute_real_frequency(dev_i2s_1);
Davide Aliprandi 4:21603d68bcf7 382 float f2 = compute_real_frequency(dev_i2s_2);
Davide Aliprandi 4:21603d68bcf7 383
Davide Aliprandi 4:21603d68bcf7 384 //printf("REAL: %f %f\r\n", f1, f2);
Davide Aliprandi 4:21603d68bcf7 385
Davide Aliprandi 4:21603d68bcf7 386 /* Compute the desired frequencies so that they are multiple one of the
Davide Aliprandi 4:21603d68bcf7 387 other. */
Davide Aliprandi 4:21603d68bcf7 388 float q;
Davide Aliprandi 4:21603d68bcf7 389 if (f1 < f2)
Davide Aliprandi 4:21603d68bcf7 390 q = f2 / f1;
Davide Aliprandi 4:21603d68bcf7 391 else
Davide Aliprandi 4:21603d68bcf7 392 q = f1 / f2;
Davide Aliprandi 4:21603d68bcf7 393 float r = q - (uint32_t) q;
Davide Aliprandi 4:21603d68bcf7 394
Davide Aliprandi 4:21603d68bcf7 395 if (r > 0)
Davide Aliprandi 4:21603d68bcf7 396 {
Davide Aliprandi 4:21603d68bcf7 397 if (f1 < f2)
Davide Aliprandi 4:21603d68bcf7 398 {
Davide Aliprandi 4:21603d68bcf7 399 float mf = compute_magic_factor(dev_i2s_1, f2 / 2);
Davide Aliprandi 4:21603d68bcf7 400 r = mf - (uint32_t) mf;
Davide Aliprandi 4:21603d68bcf7 401 if (r > 0)
Davide Aliprandi 4:21603d68bcf7 402 {
Davide Aliprandi 4:21603d68bcf7 403 if (f2 > dev_i2s_2->_hz)
Davide Aliprandi 4:21603d68bcf7 404 f1 = compute_desired_frequency(dev_i2s_1, ((uint32_t) mf) + 1);
Davide Aliprandi 4:21603d68bcf7 405 else
Davide Aliprandi 4:21603d68bcf7 406 f1 = compute_desired_frequency(dev_i2s_1, ((uint32_t) mf) - 1);
Davide Aliprandi 4:21603d68bcf7 407 f2 = 2 * f1;
Davide Aliprandi 4:21603d68bcf7 408 }
Davide Aliprandi 4:21603d68bcf7 409 else
Davide Aliprandi 4:21603d68bcf7 410 f1 = f2 / 2;
Davide Aliprandi 4:21603d68bcf7 411 }
Davide Aliprandi 4:21603d68bcf7 412 else
Davide Aliprandi 4:21603d68bcf7 413 {
Davide Aliprandi 4:21603d68bcf7 414 float mf = compute_magic_factor(dev_i2s_2, f1 / 2);
Davide Aliprandi 4:21603d68bcf7 415 r = mf - (uint32_t) mf;
Davide Aliprandi 4:21603d68bcf7 416 if (r > 0)
Davide Aliprandi 4:21603d68bcf7 417 {
Davide Aliprandi 4:21603d68bcf7 418 if (f1 > dev_i2s_1->_hz)
Davide Aliprandi 4:21603d68bcf7 419 f2 = compute_desired_frequency(dev_i2s_2, ((uint32_t) mf) + 1);
Davide Aliprandi 4:21603d68bcf7 420 else
Davide Aliprandi 4:21603d68bcf7 421 f2 = compute_desired_frequency(dev_i2s_2, ((uint32_t) mf) - 1);
Davide Aliprandi 4:21603d68bcf7 422 f1 = 2 * f2;
Davide Aliprandi 4:21603d68bcf7 423 }
Davide Aliprandi 4:21603d68bcf7 424 else
Davide Aliprandi 4:21603d68bcf7 425 f2 = f1 / 2;
Davide Aliprandi 4:21603d68bcf7 426 }
Davide Aliprandi 4:21603d68bcf7 427 }
Davide Aliprandi 4:21603d68bcf7 428
Davide Aliprandi 4:21603d68bcf7 429 //printf("DESIRED: %f %f\r\n", f1, f2);
Davide Aliprandi 4:21603d68bcf7 430
Davide Aliprandi 4:21603d68bcf7 431 /* Set the desired frequencies. */
Davide Aliprandi 4:21603d68bcf7 432 dev_i2s_1->audio_frequency(f1);
Davide Aliprandi 4:21603d68bcf7 433 dev_i2s_2->audio_frequency(f2);
Davide Aliprandi 4:21603d68bcf7 434
Davide Aliprandi 4:21603d68bcf7 435 return 0;
Davide Aliprandi 4:21603d68bcf7 436 }
Davide Aliprandi 4:21603d68bcf7 437
Wolfgang Betz 0:752e74bf5ef1 438 } // namespace mbed
Wolfgang Betz 0:752e74bf5ef1 439
Wolfgang Betz 0:752e74bf5ef1 440 #endif