1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! This is a platform agnostic Rust driver for the Si4702 and Si4703 FM radio
//! turners (receivers) using the [`embedded-hal`] traits and I2C.
//!
//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
//!
//! This driver allows you to:
//! - Enable/disable the device. See: [`enable()`].
//! - Mute/unmute. See: [`mute()`].
//! - Configure seek. See: [`configure_seek()`].
//! - Seek with/without STC interrupts. See: [`seek_with_stc_int_pin()`].
//! - Tune a frequency with/without STC interrupts. See: [`tune_with_stc_int_pin()`].
//! - Set volume. See: [`set_volume()`].
//! - Set band. See: [`set_band()`].
//! - Set channel spacing. See: [`set_channel_spacing()`].
//! - Set the GPIO1, GPIO2 and GPIO3 function/status. See: [`set_gpio1()`].
//! - Enable/disable softmute. See: [`enable_softmute()`].
//! - Enable/disable auto gain control. See: [`enable_auto_gain_control()`].
//! - Enable/disable oscillator. See: [`enable_oscillator()`].
//! - Enable/disable STC interrupts. See: [`enable_stc_interrupts()`].
//! - Enable/disable audio High-Z. See: [`enable_audio_high_z()`].
//! - Set de-emphasis. See: [`set_deemphasis()`].
//! - Set stereo to mono blend level. See: [`set_stereo_to_mono_blend_level()`].
//! - Set stereo/mono output mode. See: [`set_output_mode()`].
//! - Read output mode. See: [`output_mode()`].
//! - Read channel. See: [`channel()`].
//! - Read device ID. See: [`device_id()`].
//! - Read chip ID. See: [`chip_id()`].
//! - Reset and select I2C communication using several methods. See: [`reset_and_select_i2c_method1()`].
//! - RDS/RBDS (only on Si4703):
//!     - Enable/disable RDS. See: [`enable_rds()`].
//!     - Enable/disable RDS interrupts. See: [`enable_rds_interrupts()`].
//!     - Read whether a new RDS group is ready. See: [`rds_ready()`].
//!     - Read whether RDS is synchronized. See: [`rds_synchronized()`].
//!     - Read RDS data. See: [`rds_data()`].
//!     - Decode RDS radio text from RDS data. See: [`get_rds_radio_text()`].
//!     - Fill char array with decoded RDS radio text from RDS data. See: [`fill_with_rds_radio_text()`].
//!
//! [`enable()`]: struct.Si4703.html#method.enable
//! [`mute()`]: struct.Si4703.html#method.mute
//! [`configure_seek()`]: struct.Si4703.html#method.configure_seek
//! [`seek_with_stc_int_pin()`]: struct.Si4703.html#method.seek_with_stc_int_pin
//! [`tune_with_stc_int_pin()`]: struct.Si4703.html#method.tune_with_stc_int_pin
//! [`set_volume()`]: struct.Si4703.html#method.set_volume
//! [`set_band()`]: struct.Si4703.html#method.set_band
//! [`set_channel_spacing()`]: struct.Si4703.html#method.set_channel_spacing
//! [`set_gpio1()`]: struct.Si4703.html#method.set_gpio1
//! [`enable_softmute()`]: struct.Si4703.html#method.enable_softmute
//! [`enable_auto_gain_control()`]: struct.Si4703.html#method.enable_auto_gain_control
//! [`enable_oscillator()`]: struct.Si4703.html#method.enable_oscillator
//! [`enable_stc_interrupts()`]: struct.Si4703.html#method.enable_stc_interrupts
//! [`enable_audio_high_z()`]: struct.Si4703.html#method.enable_audio_high_z
//! [`set_deemphasis()`]: struct.Si4703.html#method.set_deemphasis
//! [`set_stereo_to_mono_blend_level()`]: struct.Si4703.html#method.set_stereo_to_mono_blend_level
//! [`set_output_mode()`]: struct.Si4703.html#method.set_output_mode
//! [`output_mode()`]: struct.Si4703.html#method.output_mode
//! [`channel()`]: struct.Si4703.html#method.channel
//! [`device_id()`]: struct.Si4703.html#method.device_id
//! [`chip_id()`]: struct.Si4703.html#method.chip_id
//! [`reset_and_select_i2c_method1()`]: struct.Si4703.html#method.reset_and_select_i2c_method1
//! [`enable_rds()`]: struct.Si4703.html#method.enable_rds
//! [`enable_rds_interrupts()`]: struct.Si4703.html#method.enable_rds_interrupts
//! [`rds_ready()`]: struct.Si4703.html#method.rds_ready
//! [`rds_synchronized()`]: struct.Si4703.html#method.rds_synchronized
//! [`rds_data()`]: struct.Si4703.html#method.rds_data
//! [`get_rds_radio_text()`]: struct.Si4703.html#method.get_rds_radio_text
//! [`fill_with_rds_radio_text()`]: struct.Si4703.html#method.fill_with_rds_radio_text
//!
//! [Introductory blog post](https://blog.eldruin.com/si4703-fm-radio-receiver-driver-in-rust/)
//! 
//! ## The devices
//!
//! The Si4702/03-C19 extends Silicon Laboratories Si4700/Si4701 FM tuner
//! family, and further increases the ease and attractiveness of adding FM
//! radio reception to mobile devices through small size and board area,
//! minimum component count, flexible programmability, and superior, proven
//! performance.
//!
//! The device offers significant programmability, and caters to the
//! subjective nature of FM listeners and variable FM broadcast environments
//! world-wide through a simplified programming interface and
//! mature functionality.
//!
//! The Si4703-C incorporates a digital processor for the European Radio Data
//! System (RDS) and the US Radio Broadcast Data System (RBDS) including all
//! required symbol decoding, block synchronization, error detection, and
//! error correction functions.
//!
//! RDS enables data such as station identification and song name to be
//! displayed to the user. The Si4703-C offers a detailed RDS view and a
//! standard view, allowing adopters to selectively choose granularity of RDS
//! status, data, and block errors.
//!
//! Datasheets:
//! - [Si4702/Si4703](https://www.silabs.com/documents/public/data-sheets/Si4702-03-C19.pdf)
//!
//! Further documentation:
//! - [Si4700/01/02/03 Programmer's Guide](https://www.silabs.com/documents/public/application-notes/AN230.pdf)
//! - [Using RDS/RBDS with the Si4701/03](https://www.silabs.com/documents/public/application-notes/AN243.pdf)
//! - [Si47xx Programming Guide](https://www.silabs.com/documents/public/application-notes/AN332.pdf)
//!
//! ## Usage (see also examples folder)
//!
//! To use this driver, import this crate and an `embedded_hal` implementation,
//! then instantiate the appropriate device.
//! In the following examples an instance of the device Si4703 will be created
//! as an example. An instance of the Si4702 device can be created with:
//! `Si4703::new_si4702(...)`.
//!
//! Please find additional examples using hardware in this repository: [driver-examples].
//!
//! [driver-examples]: https://github.com/eldruin/driver-examples
//!
//! ### Seek a channel, listen for 5 seconds, then seek again
//!
//! ```no_run
//! use embedded_hal::blocking::delay::DelayMs;
//! use linux_embedded_hal::{Delay, I2cdev, Pin};
//! use si4703::{
//!     reset_and_select_i2c_method1, ChannelSpacing, DeEmphasis, ErrorWithPin, SeekDirection,
//!     SeekMode, Si4703, Volume,
//! };
//!
//! # fn main() {
//! let mut delay = Delay {};
//! {
//!     // Reset and communication protocol selection must be done beforehand
//!     let mut sda = Pin::new(2);
//!     let mut rst = Pin::new(17);
//!     reset_and_select_i2c_method1(&mut rst, &mut sda, &mut delay).unwrap();
//! }
//! let dev = I2cdev::new("/dev/i2c-1").unwrap();
//! let mut radio = Si4703::new(dev);
//! radio.enable_oscillator().unwrap();
//! // Wait for the oscillator to stabilize
//! delay.delay_ms(500_u16);
//! radio.enable().unwrap();
//! // Wait for powerup
//! delay.delay_ms(110_u16);
//!
//! radio.set_volume(Volume::Dbfsm28).unwrap();
//! radio.set_deemphasis(DeEmphasis::Us50).unwrap();
//! radio.set_channel_spacing(ChannelSpacing::Khz100).unwrap();
//! radio.unmute().unwrap();
//!
//! // use STC interrupt pin method
//! let stc_int = Pin::new(27);
//! loop {
//!     match radio.seek_with_stc_int_pin(SeekMode::Wrap, SeekDirection::Up, &stc_int) {
//!         Err(nb::Error::WouldBlock) => {
//!             let channel = radio.channel().unwrap_or(-1.0);
//!             println!("Trying channel at {:1} MHz", channel);
//!         }
//!         Err(nb::Error::Other(ErrorWithPin::SeekFailed)) => {
//!             println!("Seek Failed");
//!         }
//!         Err(e) => {
//!             println!("Error: {:?}", e);
//!         }
//!         Ok(_) => {
//!             let channel = radio.channel().unwrap_or(-1.0);
//!             println!("Found channel at {:1} MHz", channel);
//!             delay.delay_ms(5000_u16); // listen for 5 seconds, then seek again
//!         }
//!     }
//!     delay.delay_ms(50_u8);
//! }
//! # }
//! ```
//!

#![deny(unsafe_code, missing_docs)]
#![no_std]

mod device_impl;
mod rds;
pub use crate::rds::{fill_with_rds_radio_text, get_rds_radio_text};
mod register_access;
mod reset;
mod seek;
use crate::register_access::{BitFlags, Register};
pub use crate::reset::{
    reset_and_select_i2c_method1, reset_and_select_i2c_method1_with_gpio3,
    reset_and_select_i2c_method2,
};
mod tune;
mod types;
use crate::types::OperationState;
pub use crate::types::{
    ic, marker, Band, ChannelSpacing, DeEmphasis, Error, ErrorWithPin, Gpio1Config, Gpio2Config,
    Gpio3Config, OutputMode, RdsBlockData, RdsBlockErrors, RdsData, RdsMode, RdsRadioText,
    RdsRadioTextData, SeekDirection, SeekFmImpulseThreshold, SeekMode, SeekSnrThreshold, Si4703,
    SoftmuteAttenuation, SoftmuteRate, StereoToMonoBlendLevel, TuneChannel, Volume,
};

impl marker::WithRds for ic::Si4703 {}

mod private {
    use super::ic;
    pub trait Sealed {}

    impl Sealed for ic::Si4702 {}
    impl Sealed for ic::Si4703 {}
}