From: Kevin Townsend Date: Fri, 4 May 2012 14:53:11 +0000 (+0200) Subject: First commit X-Git-Url: https://git.rohieb.name/hackover2013-badge-firmware.git/commitdiff_plain/3da0b84d086be43fc78b4a4b75828babacd0cd13 First commit --- diff --git a/drivers/audio/tea5767/tea5767.c b/drivers/audio/tea5767/tea5767.c new file mode 100644 index 0000000..4e141bf --- /dev/null +++ b/drivers/audio/tea5767/tea5767.c @@ -0,0 +1,279 @@ +/**************************************************************************/ +/*! + @file tea5767.c + @author K. Townsend + + @brief Driver for the TEA5767 FM receiver. + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2012, K. Townsend + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#include "tea5767.h" +#include "core/i2c/i2c.h" +#include "core/systick/systick.h" + +extern volatile uint8_t I2CMasterBuffer[I2C_BUFSIZE]; +extern volatile uint8_t I2CSlaveBuffer[I2C_BUFSIZE]; + +static bool _tea5767Initialised = false; + +/**************************************************************************/ +/*! + @brief Sends 5 bytes over I2C +*/ +/**************************************************************************/ +void tea5767SendData(uint8_t * bytes) +{ + // Clear write buffers + uint32_t i; + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + } + + I2CWriteLength = 6; + I2CReadLength = 0; + I2CMasterBuffer[0] = TEA5767_ADDRESS; + I2CMasterBuffer[1] = bytes[0]; + I2CMasterBuffer[2] = bytes[1]; + I2CMasterBuffer[3] = bytes[2]; + I2CMasterBuffer[4] = bytes[3]; + I2CMasterBuffer[5] = bytes[4]; + i2cEngine(); +} + +/**************************************************************************/ +/*! + @brief Reads 5 bytes over I2C +*/ +/**************************************************************************/ +void tea5767ReadData(uint8_t * bytes) +{ + // Clear buffers + uint32_t i; + for ( i = 0; i < I2C_BUFSIZE; i++ ) + { + I2CMasterBuffer[i] = 0x00; + I2CSlaveBuffer[i] = 0x00; + } + + I2CWriteLength = 1; + I2CReadLength = 5; + I2CMasterBuffer[0] = TEA5767_ADDRESS | TEA5767_READ; + i2cEngine(); + + bytes[0] = I2CSlaveBuffer[0]; + bytes[1] = I2CSlaveBuffer[1]; + bytes[2] = I2CSlaveBuffer[2]; + bytes[3] = I2CSlaveBuffer[3]; + bytes[4] = I2CSlaveBuffer[4]; +} + +/**************************************************************************/ +/*! + @brief This is a test function to evaluate the quality of crystals + used on 3rd party modules. Most modules use cheap 32.768kHz + crystals which are prone to de-tuning. Checking the IF bits + at 81.4MHz can check if the crystal is problematic or not. + + @returns True (1) if the IF bits are equal to 0x37 (good), otherwise + false (0). The device may still function if the IF values + are slightly off, but auto-scan and tuning will likely be + less reliable. +*/ +/**************************************************************************/ +bool tea5767CheckCrystal(void) +{ + /* AN10133 (p.38) states: + + The choice of 32768Hz reference frequency makes it possible to use a + cheap 32.768kHz watch crystal. A drawback of these clocks is that they + have a very high second order temperature coefficient. This may result + in de-tuning the radio or a search action may fail. + + Care should be taken when using this crystal. The accuracy of the + 32768Hz crystal can be checked by tuning the radio to 81.4 MHz + with high/low side injection and reading the IF via the bus. The IF + must be 37Hex. + + An other issue when using this crystal is the grid position. It is + not possible to create a 100kHz grid position, but 98.304kHz + (3*32768Hz). This should not be a problem if this is resolved in + software. + + The motional capacitance of the 32768Hz crystal should be between + 1.5fF and 3fF. Shunt capacitance must be max 3.5pF. The series + resistance should not exceed 75KOhm. + + Further, the frequency accuracy of this crystal must not exceed ±20 + ppm, while the temperature drift should be in the order of ±50 ppm + over a temperature range of –10oC to +60oC. + */ + + uint8_t ifValue = 0; + uint8_t buffer[5] = { 0, 0, 0, 0, 0 }; + + // Set the frequency to 81.4MHz + tea5767SetFrequency(81400000); + systickDelay(100); + + // Read back the IF bits + tea5767ReadData(&buffer[0]); + ifValue = buffer[2] & 0x7F; + + // Reconfigure the chip to a known frequency to avoid user problems + // tea5767SetFrequency(TEA5767_FMBANDSTART_JAPAN); + tea5767SetFrequency(TEA5767_FMBANDSTART_US_EUROPE); + + // Return true if the crystal is OK (IF = 0x37 @ 81.4MHz), + // false if it's something else + if (0x37 == ifValue) + return true; + else + return false; +} + +/**************************************************************************/ +/*! + @brief Initialises I2C for the TEA5767. +*/ +/**************************************************************************/ +uint32_t tea5767Init() +{ + // Initialise I2C + if (i2cInit(I2CMASTER) == false) + { + /* Fatal error */ + return -1; + } + + /* Set initialisation flag */ + _tea5767Initialised = true; + + // Set the frequency to a known value to make sure the PLL is properly configured + // tea5767SetFrequency(TEA5767_FMBANDSTART_JAPAN); + tea5767SetFrequency(TEA5767_FMBANDSTART_US_EUROPE); + + return 0; +} + +/**************************************************************************/ +/*! + @brief Sets the frequency to the specified value in Hz + + @param[in] frequency + The frequency in Hz, meaning that 95.7MHz = 97,500,000 + 101.4 MHz = 101,400,000 etc. +*/ +/**************************************************************************/ +void tea5767SetFrequency( uint32_t frequency ) +{ + uint32_t pllValue; + uint8_t buffer[5] = { 0, 0, 0, 0, 0 }; + + // Make sure I2C is initialised + if (!_tea5767Initialised) tea5767Init(); + + // Calculate PLL word for high side injection mode + // NDEC = (4*(FRF + FIF)) / FREFS + // where: + // FRF = Desired tuning frequency in Hz + // FIF = Intermediate frequency in Hz (225kHz) + // FREFS = Reference frequency in Hz (32.768kHz) + pllValue = (4 * (frequency + 225000)) / 32768; + + buffer[0] = (pllValue >> 8) & 0x3F; // Upper 6 PLL bits (also turns mute and search mode off!) + buffer[1] = (pllValue & 0xFF); // Lower 8 PLL bits + buffer[2] = TEA5767_WBYTE3_HLSI; // High side injection mode + buffer[3] = TEA5767_WBYTE4_XTAL; // XTAL bit = 1 for 32.768kHz crystal + buffer[4] = 0; // PLLREF bit = 0 for 32.768kHz crystal + + // Send data over I2C + tea5767SendData(buffer); +} + +/**************************************************************************/ +/*! + @brief Returns the current frequency in Hz (meaning 97.5MHz will be + returned as 97,500,000 etc. +*/ +/**************************************************************************/ +uint32_t tea5767GetFrequency( void ) +{ + uint32_t frequency = 0; + uint8_t buffer[5] = { 0, 0, 0, 0, 0 }; + + if (!_tea5767Initialised) tea5767Init(); + + tea5767ReadData(&buffer[0]); + + // Retrieve the raw 14-bit PLL value from word 1 and 2 + frequency = ((buffer[0] & 0x3F) << 8) + buffer[1]; + // Determine the current frequency using the same high side formula as above + frequency = frequency * 32768 / 4 - 225000; + + return frequency; +} + +/**************************************************************************/ +/*! + @brief Starts the auto-scan process from the current frequency + + @param[in] scanDirection + Set this to 0 to scan down, or one to scan up, starting + at the current frequency. +*/ +/**************************************************************************/ +void tea5767Scan( uint8_t scanDirection ) +{ + uint8_t rbuffer[5] = { 0, 0, 0, 0, 0 }; + uint8_t wbuffer[5] = { 0, 0, 0, 0, 0 }; + + // Make sure I2C is initialised + if (!_tea5767Initialised) tea5767Init(); + + // First we need to get the current PLL word to know where to start from + tea5767ReadData(&rbuffer[0]); + + // Set the PLL value again and append the search enable bu + wbuffer[0] = TEA5767_WBYTE1_SEARCHMODE + // Search mode enabled + (rbuffer[0] & 0x3F); // Upper PLL bits + wbuffer[1] = rbuffer[1]; // Lower PLL bits + wbuffer[2] = TEA5767_WBYTE3_SEARCHSTOPLEVEL_MID | // Mid level ADC for search steps + TEA5767_WBYTE3_HLSI; // HLSI (must be 1 for PLL) + wbuffer[3] = TEA5767_WBYTE4_XTAL; // Must be 1 for 32.768kHz crystal + wbuffer[4] = 0x00; + + // Set the scan direction bit to 1 (scan up) if scanDirection is non-zero + if (scanDirection) wbuffer[2] |= TEA5767_WBYTE3_SEARCHUPDOWN; + + // Send data over I2C + tea5767SendData(wbuffer); +} diff --git a/drivers/audio/tea5767/tea5767.h b/drivers/audio/tea5767/tea5767.h new file mode 100644 index 0000000..f2a4869 --- /dev/null +++ b/drivers/audio/tea5767/tea5767.h @@ -0,0 +1,149 @@ +/**************************************************************************/ +/*! + @file tea5767.h + @author K. Townsend + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2012, K. Townsend. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef _TEA5767_H_ +#define _TEA5767_H_ + +#include "projectconfig.h" + +#define TEA5767_FMBANDSTART_US_EUROPE (87500000) // 87.5 MHz to 108 MHz +#define TEA5767_FMBANDSTART_JAPAN (76000000) // 76 MHz to 91 MHz plus TV audio at 108 MHz + +/*========================================================================= + I2C ADDRESS/BITS + -----------------------------------------------------------------------*/ + #define TEA5767_ADDRESS (0xC0) // 1100000x + #define TEA5767_READ (0x01) +/*=========================================================================*/ + +/*========================================================================= + WRITE BYTE 1 + MUTE | SEARCHMODE | PLL13 | PLL12 | PLL11 | PLL10 | PLL9 | PLL8 + -----------------------------------------------------------------------*/ + #define TEA5767_WBYTE1_MUTE (1<<7) // 1 = mute, 0 = output enabled (mute enabled after reset) + #define TEA5767_WBYTE1_SEARCHMODE (1<<6) // 1 = Search mode enabled +/*=========================================================================*/ + +/*========================================================================= + WRITE BYTE 2 + PLL7 | PLL6 | PLL5 | PLL4 | PLL3 | PLL2 | PLL1 | PLL0 + -----------------------------------------------------------------------*/ + +/*=========================================================================*/ + +/*========================================================================= + WRITE BYTE 3 + SUD | SSL1 | SSL0 | HLSI | MS | MR | ML | SWP1 + -----------------------------------------------------------------------*/ + #define TEA5767_WBYTE3_SEARCHUPDOWN (1<<7) // 1 = search up, 0 = search down + #define TEA5767_WBYTE3_SEARCHSTOPLEVEL1 (1<<6) // 10 = mid level (ADC = 7), 11 = high level (ADC = 10) + #define TEA5767_WBYTE3_SEARCHSTOPLEVEL0 (1<<5) // 00 = invalid, 01 = low level (ADC = 5) + #define TEA5767_WBYTE3_SEARCHSTOPLEVELMASK (3<<5) + #define TEA5767_WBYTE3_HLSI (1<<4) // 1 = high side LO injection, 0 = low side LO injection + #define TEA5767_WBYTE3_MONOTOSTEREO (1<<3) // 1 = force mono, 0 = stereo on + #define TEA5767_WBYTE3_MUTERIGHT (1<<2) // 1 = mute right audio, 0 = enabled + #define TEA5767_WBYTE3_MUTELEFT (1<<1) // 1 = mute left audio, 0 = enabled + /*---------------------------------------------------------------------*/ + #define TEA5767_WBYTE3_SEARCHSTOPLEVEL_LOW (0x20) // ADC output = 5 (bit: 01) + #define TEA5767_WBYTE3_SEARCHSTOPLEVEL_MID (0x40) // ADC output = 7 (bit: 10) + #define TEA5767_WBYTE3_SEARCHSTOPLEVEL_HIGH (0x60) // ADC output = 10 (bit: 11) +/*=========================================================================*/ + +/*========================================================================= + WRITE BYTE 4 + SWP2 | STBY | BL | XTAL | SMUTE | HCC | SNC | SI + -----------------------------------------------------------------------*/ + #define TEA5767_WBYTE4_STANDBY (1<<6) // 1 = standby mode + #define TEA5767_WBYTE4_BANDLIMITS (1<<5) // 1 = Japanese FM band, 0 = US/Europe + #define TEA5767_WBYTE4_XTAL (1<<4) // Combined with PLLREF in byte 5 (set to 1 for 32.768kHz crystal) + #define TEA5767_WBYTE4_SOFTMUTE (1<<3) // 1 = soft mute enabled + #define TEA5767_WBYTE4_HIGHCUTCONTROL (1<<2) // 1 = HCC enabled + #define TEA5767_WBYTE4_STEREONOISECANCEL (1<<1) // 1 = stereo noise cancelling enabled +/*=========================================================================*/ + +/*========================================================================= + WRITE BYTE 5 + PLLREF | DTC | - | - | - | - | - | - + -----------------------------------------------------------------------*/ + #define TEA5767_WBYTE5_PLLREF (1<<7) // 1 = 6.5MHz PLL ref freq. enabled (set to 0 for 32.768kHz crystal) + #define TEA5767_WBYTE5_DEEMPHASISTIMECONST (1<<6) // 1 = DTC is 75µs, 0 = 50µs +/*=========================================================================*/ + + +/*========================================================================= + READ BYTE 1 + RF | BLF | PLL13 | PLL12 | PLL11 | PLL 10 | PLL9 | PLL8 + -----------------------------------------------------------------------*/ + #define TEA5767_RBYTE1_READYFLAG (1<<7) // 1 = station found or band-limit reached, 0 = no station found + #define TEA5767_RBYTE1_BANDLIMITFLAG (1<<6) // 1 = band limit has been reached, 0 = band limit not reached +/*=========================================================================*/ + +/*========================================================================= + READ BYTE 2 + PLL7 | PLL6 | PLL5 | PLL4 | PLL3 | PLL2 | PLL1 | PLL0 + -----------------------------------------------------------------------*/ + +/*=========================================================================*/ + +/*========================================================================= + READ BYTE 3 + STEREO | IF6 | IF5 | IF4 | IF3 | IF2 | IF1 | IF0 + -----------------------------------------------------------------------*/ + #define TEA5767_RBYTE3_STEREOINDICATOR (1<<7) // 1 = stereo reception, 0 = mono reception +/*=========================================================================*/ + +/*========================================================================= + READ BYTE 4 + LEV3 | LEV2 | LEV1 | LEV0 | CI3 | CI2 | CI1 | - + -----------------------------------------------------------------------*/ + #define TEA5767_RBYTE4_ADCLEVELOUTPUTMASK (0xF0) // ADC output level + #define TEA5767_RBYTE4_CHIPIDMASK (0x0F) // These bits must be set to 0! +/*=========================================================================*/ + +/*========================================================================= + READ BYTE 5 + - | - | - | - | - | - | - | - + -----------------------------------------------------------------------*/ + +/*=========================================================================*/ + +uint32_t tea5767Init( void ); +void tea5767SetFrequency( uint32_t ); +uint32_t tea5767GetFrequency( void ); +void tea5767Scan( uint8_t ); +void tea5767Mute( bool ); + +#endif \ No newline at end of file