/** Internal Espressif SDK "PHY info" data structure The data structure (sdk_phy_info_t) is used to configure the ESP8266 PHY layer via the SDK. The fields here are not written directly to hardware, the SDK code (mostly in libphy) parses this structure and configures the hardware. The structure loaded at reset time from a flash configuration sector (see read_saved_phy_info()) (Espressif's SDK sources this from a file "esp_init_data_default.bin"). If no valid structure is found in the flash config sector then the SDK loads default values (see get_default_phy_info()). It is possible to implement a custom get_default_phy_info() to change the PHY default settings (see the 'version' field below). @note It is possible that the SDK will quietly write a new configuration sector to flash itself following internal calibration, etc. However this does not seem to happen, you need to flash it explicitly if you want it stored there. @note Most of what is below is unconfirmed, except where a @note says that it has been confirmed to work as expected. Please consider submitting notes if you find behaviour here that works or doesn't work as expected. Information on the meaning/offset of fields comes from Espressif's flash download tool, which uses an Excel spreadsheet (in the init_data directory of the ZIP file) to configure and a Python script to convert an esp_init_data_custom.bin file to flash: http://bbs.espressif.com/viewtopic.php?f=5&t=433 Many fields remain undocumented (& disassembly of libphy suggests that some documented fields supported undocumented values.) A few additional notes about the phy_info fields can be found in the ESP Arduino ESP8266 phy_init_data structure (however most of that content is verbatim from Espressif's spreadsheet): https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_phy.c#L29 Part of esp-open-rtos. Copyright (C) 2016 Angus Gratton, BSD Licensed as described in the file LICENSE. */ #ifndef _ESPRESSIF_PHY_INFO_H #define _ESPRESSIF_PHY_INFO_H #include #include #include /* CRYSTAL_FREQ_xx values as used by sdk_phy_info_t.crystal_freq */ #define CRYSTAL_FREQ_40M 0 #define CRYSTAL_FREQ_26M 1 #define CRYSTAL_FREQ_24M 2 /* SDIO_CONFIG_xx values as used by sdk_phy_info_t.sdio_config */ #define SDIO_CONFIG_AUTO 0 /* Uses pin strapping to determine */ #define SDIO_CONFIG_SDIOV1_1 /* Data output on negative edge */ #define SDIO_CONFIG_SDIOV2_0 /* data output on positive edge */ /* BT_COEXIST_CONFIG_xx values as used by sdk_phy_info_t.bt_coexist */ /* No bluetooth */ #define BT_COEXIST_CONFIG_NONE 0 /* Coexistence configuration A: GPIO 0 - WLAN_ACTIVE GPIO 14 - BT_ACTIVE GPIO 13 - BT_PRIORITY GPIO 3 - ANT_SEL_BT */ #define BT_COEXIST_CONFIG_A 1 /* No coexistence, but Bluetooth enabled? Unsure how this works? */ #define BT_COEXIST_CONFIG_PRESENT 2 /* Coexistence configuration B: GPIO 0 - WLAN_ACTIVE GPIO 14 - BT_PRIORITY GPIO 13 - BT_ACTIVE GPIO 3 - ANT_SEL_BT */ #define BT_COEXIST_CONFIG_B 3 /* BT_COEXIST_PROTOCOL_xx values for coexistence protocol, field sdk_phy_info_t.bt_coexist_protocol */ #define BT_COEXIST_PROTOCOL_WIFI_ONLY 0 #define BT_COEXIST_PROTOCOL_BT_ONLY 1 /* Coexistence is enabled, Bluetooth has its own antenna */ #define BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT 2 /* Coexistence is enabled, Bluetooth shares WiFi antenna */ #define BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT 4 /* Coexistence is enabled, use only BT_ACTIVE signal */ #define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY 0 /* Coexistence is enabled, use both BT_ACTIVE and BT_PRIORITY signals */ #define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_PRIORITY 1 /* DUAL_ANT_CONFIG_xx values for dual antenna config, field sdk_phy_info_t.dual_ant_config (Not really clear how this feature works, if at all.) */ #define DUAL_ANT_CONFIG_NONE 0 /* antenna diversity for WiFi, use GPIO0 + U0RXD (?) */ #define DUAL_ANT_CONFIG_DUAL 1 /* TX/RX switch for external PA & LNA: GPIO 0 high, GPIO 3 low during TX */ #define DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW /* TX/RX switch for external PA & LNA: GPIO 0 low, GPIO 3 high during TX */ #define DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH /* CRYSTAL_SLEEP_xx values used for sdk_phy_info_t.crystal_sleep */ #define CRYSTAL_SLEEP_OFF 0 #define CRYSTAL_SLEEP_ON 1 #define CRYSTAL_SLEEP_GPIO16 2 #define CRYSTAL_SLEEP_GPIO2 3 /* RF Stage 0 & 1 attenuation constants. Use for sdk_phy_info_t.lp_atten_stage01 @note These values have been tested and are confirmed to work as expected by measuring RSSI w/ rt73 USB adapter in monitor mode (some values also checked on spectrum analyzer) - provided low_power_en is set then the signal is attenuated as per this setting. (It may look like LP_ATTEN_STAGE01_11_5DB is out of order, but according to monitor mode captures this is the correct ordering of these constants.) Setting the numeric values in between these constants appears to also attenuate the signal, but not necessarily by the amount you'd expect. */ #define LP_ATTEN_STAGE01_0DB 0x0f /* 0dB */ #define LP_ATTEN_STAGE01_2_5DB 0x0e /* -2.5dB */ #define LP_ATTEN_STAGE01_6DB 0x0d /* -6dB */ #define LP_ATTEN_STAGE01_8_5DB 0x09 /* -8.5dB */ #define LP_ATTEN_STAGE01_11_5DB 0x0c /* -11.5dB */ #define LP_ATTEN_STAGE01_14DB 0x08 /* -14dB */ #define LP_ATTEN_STAGE01_17_5DB 0x04 /* -17.5dB */ #define LP_ATTEN_STAGE01_23DB 0x00 /* -23dB */ /* Constant for sdk_phy_info_t.pa_vdd */ #define PA_VDD_MEASURE_VCC 0xFF /* Bitmask flags for sdk_phy_info_t.freq_correct_mode */ /* Set this flag to disable frequency offset correction */ #define FREQ_CORRECT_DISABLE 0 /* Set this flag to enable frequency offset correction */ #define FREQ_CORRECT_ENABLE BIT(0) /* Set = Baseband PLL frequency is 160MHz (can only apply +ve offset) * Unset = Baseband PLL frequency is 168MHz (can apply +ve/-ve offset */ #define FREQ_CORRECT_BB_160M BIT(1) /* Set = use force_freq_offset field to correct, Unset = automatically measure & correct offset */ #define FREQ_CORRECT_FORCE BIT(2) /* RF_CAL_MODE_xx fields used for sdk_phy_info_t.rf_cal_mode */ /* Use saved RF CAL data from flash, only. RF init takes 2ms. */ #define RF_CAL_MODE_SAVED 0 /* Calibrate TX power control only, use saved RF CAL data for others. RF init takes 20ms. */ #define RF_CAL_MODE_TXPOWER_ONLY 1 /* Unclear if/how this mode is different to 2? */ #define RF_CAL_MODE_SAVED_2 2 /* Run full RF CAL routine. RF init takes approx 200ms. */ #define RF_CAL_MODE_FULL 3 /* Data structure that maps to the phy_info configuration block */ typedef struct __attribute__((packed)) { uint8_t _reserved00[0x05]; /* 0x00 - 0x04 */ /* This "version" field was set to 5 in the SDK phy_info, and the original SDK startup code checks it is 5 and then loads default PHY configuration otherwise. esp-open-rtos will load phy_info from get_default_phy_info() if the value stored in flash has a different value to the value returned in get_default_phy_info(). This means you can increment the version return by get_default_phy_info() (to any value but 0xFF), and know that the new defaults will replace any older stored values. @notes It's not clear whether this is actually a version field (the other 24 bytes here have equally arbitrary numbers in them.) Changing the "version" to other values does not seem to effect WiFi performance at all, neither does zeroing out the first 5 reserved bytes in _reserved00. However zeroing bytes in the _reserved06 region will break WiFi entirely. */ uint8_t version; /* 0x05 */ int8_t _reserved06[0x14]; /* 0x06 - 0x19 */ /* spur_freq = spur_freq_primary / spur_freq_divisor */ uint8_t spur_freq_primary; /* 0x1a */ uint8_t spur_freq_divisor; /* 0x1b */ /* Bitmask to enable spur_freq for each channel Appears to be a big endian short word? */ uint8_t spur_freq_en_h; /* 0x1c */ uint8_t spur_freq_en_l; /* 0x1d */ uint8_t _reserved1e[4]; /* 0x1e - 0x21 */ /* Each value is a target power level. Units are 1/4 dBm ie value 64 = 16dBm. SDK defaults to using these transmit powers: 20.5dBm, 19.5dBm, 18.5dBm, 17dBm, 16dBm, 14dBm @note Adjusting these values is confirmed to reduce transmit power accordingly. */ uint8_t target_power[6]; /* 0x22 - 0x27 */ /* Maps 8 MCS (modulation & coding schemes) types for 802.11b, g & * n to a target_power level index (0-5), set above. This mapping of MCS slot to MCS type is derived from the spreadsheet and also a table sent by Espressif, but is untested and may be SDK version dependendent (especially any 802.11n rates). However the general relationship is confirmed to hold (higher MCS index = higher bit rate). MCS 0: 1Mbps/2Mbps/5.5Mbps/11Mbps (802.11b) / 6Mbps/9Mbps (802.11g) default target_power 0 (default 20.5dBm) (see also pwr_ind_11b_en) MCS 1: 12Mbps (802.11g) default target_power 0 (default 20.5dBm) MCS 2: 18Mbps (802.11g) default target_power 1 (19.5dBm) MCS 3: 24Mbps (802.11g) default target_power 1 (19.5dBm) MCS 4: 36Mbps (802.11g) default target_power 2 (18.5dBm) MCS 5: 48Mbps (802.11g) default target_power 3 (17dBm) MCS 6: 54Mbps (802.11g) default target_power 4 (16dBm) MCS 7: 65Mbps (802.11n) - unclear if ever used? default target_power 5 (14dBm) */ uint8_t target_power_index_mcs[8]; /* 0x28 - 0x2f */ /* One of CRYSTAL_FREQ_40M / CRYSTAL_FREQ_26M / CRYSTAL_FREQ_24M The crystal configured here is the input to the PLL setting calculations which are used to derive the CPU & APB peripheral clock frequency, and probably the WiFi PLLs (unconfirmed.) */ uint8_t crystal_freq; /* 0x30 */ uint8_t _unused31; /* 0x31: Possibly high byte of crystal freq? */ /* One of SDIO_CONFIG_AUTO, SDIO_CONFIG_SDIOV1_1, SDIO_CONFIG_SDIOV2_0 */ uint8_t sdio_config; /* 0x32 */ /* BT coexistence pin configuration. One of BT_COEXIST_CONFIG_NONE, BT_COEXIST_CONFIG_A, BT_COEXIST_CONFIG_PRESENT, BT_COEXIST_CONFIG_B */ uint8_t bt_coexist_config; /* 0x33 */ /* BT coexistence pin protocol. If no coexistence: Either BT_COEXIST_PROTOCOL_WIFI_ONLY, or BT_COEXIST_PROTOCOL_BT_ONLY. If coexistence: Combine one of BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT or BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT with one of BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY or BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_BT_PRIORITY */ uint8_t bt_coexist_protocol; /* 0x34 */ /* Dual antenna configuration One of DUAL_ANT_CONFIG_NONE, DUAL_ANT_CONFIG_DUAL, DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW, DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH */ uint8_t dual_ant_config; /* 0x35 */ uint8_t _reserved34; /* 0x36 */ /* For sharing crystal clock with other devices: one of CRYSTAL_SLEEP_OFF, CRYSTAL_SLEEP_ON, CRYSTAL_SLEEP_GPIO16, CRYSTAL_SLEEP_GPIO2 */ uint8_t crystal_sleep; /* 0x37 */ uint8_t _unused38[8]; /* spur_freq_2 = spur_freq_2_primary / spur_freq_2_divisor */ uint8_t spur_freq_2_primary; /* 0x40 */ uint8_t spur_freq_2_divisor; /* 0x41 */ /* Bitmask to enable spur_freq_2 for each channel? Appears to be a big endian short word? */ uint8_t spur_freq_2_en_h; /* 0x42 */ uint8_t spur_freq_2_en_l; /* 0x43 */ /* Not really clear what these do */ uint8_t spur_freq_cfg_msb; /* 0x44 */ uint8_t spur_freq_2_cfg_msb; /* 0x45 */ uint16_t spur_freq_3_cfg; /* 0x46 - 0x47 */ uint16_t spur_freq_4_cfg; /* 0x48 - 0x49 */ uint8_t _reserved4a[4]; /* 0x4a - 0x4d */ uint8_t _unused78[15]; /* 0x4e - 0x5c */ /* Flag to enable low power mode */ uint8_t low_power_en; /* 0x5d */ /* Low Power attenuation of RF gain stages 0 & 1 Attenuates transmit power if/when low_power_en is set. Use one of the constants LP_ATTEN_STAGE01_0DB, LP_ATTEN_STAGE01_2_5DB, LP_ATTEN_STAGE01_6DB, LP_ATTEN_STAGE01_8_5DB, LP_ATTEN_STAGE01_11_5DB, LP_ATTEN_STAGE01_14DB, LP_ATTEN_STAGE01_17_5DB, LP_ATTEN_STAGE01_23DB. */ uint8_t lp_atten_stage01; /* 0x5e */ /* Low Power(?) attenuation of baseband gain Units are minus 1/4 dB, ie value 4 == -1dB. Maximum value is 24 (0x18) == -6dB */ uint8_t lp_atten_bb; /* 0x5f */ /* I believe this means, when pwr_ind_11b_en == 0 then the 802.11g MCS 0 level from target_power_index_mcs are used to determine 802.11b transmit power level. However, when pwr_ind_11b_en == 1 then the index values in pwr_ind_11b_0 & pwr_ind_11b_1 are used for 802.11b instead. This is all unconfirmed, if you can confirm then please update this comment. */ uint8_t pwr_ind_11b_en; /* 0x60 */ /* 802.11b low data rate power index (0~5). Sets the power level index for operation at 1 & 2Mbps */ uint8_t pwr_ind_11b_0; /* 0x61 */ /* 802.11b high data rate power index (0~5) Sets the power level index for operation at 5.5 & 11Mbps */ uint8_t pwr_ind_11b_1; /* 0x62 */ uint8_t _unused63[8]; /* 0x63 - 0x6a */ /* Set the voltage of PA_VDD, which appears to be an internal analog reference voltage(?) This field is called vdd33_const in the Arduino phy fields, and relates to usage of the TOUT pin (ADC pin). Set to PA_VDD_MEASURE_VCC (0xFF) and leave TOUT (ADC) pin floating in order to use the ADC to measure the 3.3V input voltage. Set to value in the range 18-36 (1.8V to 3.6V) to set a reference voltage(?) when using TOUT pin as an ADC input. I think this is the reference voltage used to scale the 0-1V which is allowed on the pin, in order to get an accurate reading. So it should be set to a value that matches system VCC... I think! */ uint8_t pa_vdd; /* 0x6b */ /* Disable RF calibration cycle for this many times */ uint8_t disable_rfcal_count; /* 0x6c */ uint8_t _unused6d[3]; /* Flags for frequency correction A bitmask combination of any of: FREQ_CORRECT_DISABLE, FREQ_CORRECT_ENABLE, FREQ_CORRECT_BB_160M, FREQ_CORRECT_FORCE */ uint8_t freq_correct_mode; /* 0x70 */ /* Force frequency offset adjustment (instead of auto measuring) units are 1 = 8kHz, full range +/- 1016kHz. Only used if FREQ_CORRECT_ENABLE and FREQ_CORRECT_FORCE are set in freq_correct_mode. Unclear whether setting FREQ_CORRECT_BB_160M (which allows only positive offsets) changes the usable range. */ int8_t force_freq_offset; /* 0x71 */ /* Use stored data in flash for RF calibration. This field was previously called rf_cal_use_flash. Acceptable values one of RF_CAL_MODE_SAVED, RF_CAL_MODE_TXPOWER_ONLY, RF_CAL_MODE_SAVED_2, RF_CAL_MODE_FULL. */ uint8_t rf_cal_mode; /* 0x72 */ uint8_t _unused73[13]; } sdk_phy_info_t; /* Some sanity check static assertions. These can probably be removed after this structure has been better tested. */ _Static_assert(sizeof(sdk_phy_info_t) == 128, "sdk_phy_info_t is wrong size!"); _Static_assert(offsetof(sdk_phy_info_t, version) == 5, "version at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, target_power) == 34, "target_power_qdb at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, bt_coexist_protocol) == 52, "bt_coexist_protocol at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, spur_freq_2_primary) == 64, "spur_freq_2_primary at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, lp_atten_stage01) == 94, "lp_atten_stage01 at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, pa_vdd) == 107, "pa_vdd aka vdd33_const at wrong offset"); _Static_assert(offsetof(sdk_phy_info_t, rf_cal_mode) == 114, "rf_cal_use_flash at wrong offset!"); /* Read the default PHY info into the supplied structure. This function is weak-aliased to get_sdk_default_phy_info() so you can replace it with your own if you want to vary the default values - suggested way to do this is to call get_sdk_default_phy_info() and then only update the fields you care about. The default PHY info is used at startup whenever the version field in the default sdk_phy_info_t does not match the version field stored in flash. So you can increment the version field to force a reset to defaults, regardless of what values are in flash. */ void get_default_phy_info(sdk_phy_info_t *info); /* Read the "SDK default" PHY info as used by the Espressif SDK */ void get_sdk_default_phy_info(sdk_phy_info_t *info); /* Read the PHY info currently stored in the SPI flash SDK configuration sector. This PHY info is updated by the SDK following RF calibration, etc. Note that the saved data may be corrupt - read the 'version' field to verify. */ void read_saved_phy_info(sdk_phy_info_t *info); /* Update the saved PHY info in the SPI flash. A reset is necessary to use these values. Note that the SDK may clobber these values, so it's recommended you reset ASAP after updating them. */ void write_saved_phy_info(const sdk_phy_info_t *info); /* Dump known fields in the phy info structure to stdout, if 'raw' flag is set then the raw hex values are also dumped. */ void dump_phy_info(const sdk_phy_info_t *info, bool raw); #endif