mbed library sources
Fork of mbed-src by
targets/hal/TARGET_Atmel/TARGET_SAM21/drivers/sercom/sercom.c@592:a274ee790e56, 2015-07-17 (annotated)
- Committer:
- mbed_official
- Date:
- Fri Jul 17 09:15:10 2015 +0100
- Revision:
- 592:a274ee790e56
- Parent:
- 579:53297373a894
Synchronized with git revision e7144f83a8d75df80c4877936b6ffe552b0be9e6
Full URL: https://github.com/mbedmicro/mbed/commit/e7144f83a8d75df80c4877936b6ffe552b0be9e6/
More API implementation for SAMR21
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mbed_official | 579:53297373a894 | 1 | #include "sercom.h" |
mbed_official | 579:53297373a894 | 2 | |
mbed_official | 579:53297373a894 | 3 | #define SHIFT 32 |
mbed_official | 579:53297373a894 | 4 | #define BAUD_INT_MAX 8192 |
mbed_official | 579:53297373a894 | 5 | #define BAUD_FP_MAX 8 |
mbed_official | 579:53297373a894 | 6 | |
mbed_official | 579:53297373a894 | 7 | #if !defined(__DOXYGEN__) |
mbed_official | 579:53297373a894 | 8 | /** |
mbed_official | 579:53297373a894 | 9 | * \internal Configuration structure to save current gclk status. |
mbed_official | 579:53297373a894 | 10 | */ |
mbed_official | 579:53297373a894 | 11 | struct _sercom_conf { |
mbed_official | 579:53297373a894 | 12 | /* Status of gclk generator initialization. */ |
mbed_official | 579:53297373a894 | 13 | bool generator_is_set; |
mbed_official | 579:53297373a894 | 14 | /* Sercom gclk generator used. */ |
mbed_official | 579:53297373a894 | 15 | enum gclk_generator generator_source; |
mbed_official | 579:53297373a894 | 16 | }; |
mbed_official | 579:53297373a894 | 17 | |
mbed_official | 579:53297373a894 | 18 | static struct _sercom_conf _sercom_config; |
mbed_official | 579:53297373a894 | 19 | |
mbed_official | 579:53297373a894 | 20 | |
mbed_official | 579:53297373a894 | 21 | /** |
mbed_official | 579:53297373a894 | 22 | * \internal Calculate 64 bit division, ref can be found in |
mbed_official | 579:53297373a894 | 23 | * http://en.wikipedia.org/wiki/Division_algorithm#Long_division |
mbed_official | 579:53297373a894 | 24 | */ |
mbed_official | 579:53297373a894 | 25 | static uint64_t long_division(uint64_t n, uint64_t d) |
mbed_official | 579:53297373a894 | 26 | { |
mbed_official | 579:53297373a894 | 27 | int32_t i; |
mbed_official | 579:53297373a894 | 28 | uint64_t q = 0, r = 0, bit_shift; |
mbed_official | 579:53297373a894 | 29 | for (i = 63; i >= 0; i--) { |
mbed_official | 579:53297373a894 | 30 | bit_shift = (uint64_t)1 << i; |
mbed_official | 579:53297373a894 | 31 | |
mbed_official | 579:53297373a894 | 32 | r = r << 1; |
mbed_official | 579:53297373a894 | 33 | |
mbed_official | 579:53297373a894 | 34 | if (n & bit_shift) { |
mbed_official | 579:53297373a894 | 35 | r |= 0x01; |
mbed_official | 579:53297373a894 | 36 | } |
mbed_official | 579:53297373a894 | 37 | |
mbed_official | 579:53297373a894 | 38 | if (r >= d) { |
mbed_official | 579:53297373a894 | 39 | r = r - d; |
mbed_official | 579:53297373a894 | 40 | q |= bit_shift; |
mbed_official | 579:53297373a894 | 41 | } |
mbed_official | 579:53297373a894 | 42 | } |
mbed_official | 579:53297373a894 | 43 | |
mbed_official | 579:53297373a894 | 44 | return q; |
mbed_official | 579:53297373a894 | 45 | } |
mbed_official | 579:53297373a894 | 46 | |
mbed_official | 579:53297373a894 | 47 | /** |
mbed_official | 579:53297373a894 | 48 | * \internal Calculate synchronous baudrate value (SPI/UART) |
mbed_official | 579:53297373a894 | 49 | */ |
mbed_official | 579:53297373a894 | 50 | enum status_code _sercom_get_sync_baud_val( |
mbed_official | 579:53297373a894 | 51 | const uint32_t baudrate, |
mbed_official | 579:53297373a894 | 52 | const uint32_t external_clock, |
mbed_official | 579:53297373a894 | 53 | uint16_t *const baudvalue) |
mbed_official | 579:53297373a894 | 54 | { |
mbed_official | 579:53297373a894 | 55 | /* Baud value variable */ |
mbed_official | 579:53297373a894 | 56 | uint16_t baud_calculated = 0; |
mbed_official | 579:53297373a894 | 57 | uint32_t clock_value = external_clock; |
mbed_official | 579:53297373a894 | 58 | |
mbed_official | 579:53297373a894 | 59 | |
mbed_official | 579:53297373a894 | 60 | /* Check if baudrate is outside of valid range. */ |
mbed_official | 579:53297373a894 | 61 | if (baudrate > (external_clock / 2)) { |
mbed_official | 579:53297373a894 | 62 | /* Return with error code */ |
mbed_official | 579:53297373a894 | 63 | return STATUS_ERR_BAUDRATE_UNAVAILABLE; |
mbed_official | 579:53297373a894 | 64 | } |
mbed_official | 579:53297373a894 | 65 | |
mbed_official | 579:53297373a894 | 66 | /* Calculate BAUD value from clock frequency and baudrate */ |
mbed_official | 579:53297373a894 | 67 | clock_value = external_clock / 2; |
mbed_official | 579:53297373a894 | 68 | while (clock_value >= baudrate) { |
mbed_official | 579:53297373a894 | 69 | clock_value = clock_value - baudrate; |
mbed_official | 579:53297373a894 | 70 | baud_calculated++; |
mbed_official | 579:53297373a894 | 71 | } |
mbed_official | 579:53297373a894 | 72 | baud_calculated = baud_calculated - 1; |
mbed_official | 579:53297373a894 | 73 | |
mbed_official | 579:53297373a894 | 74 | /* Check if BAUD value is more than 255, which is maximum |
mbed_official | 579:53297373a894 | 75 | * for synchronous mode */ |
mbed_official | 579:53297373a894 | 76 | if (baud_calculated > 0xFF) { |
mbed_official | 579:53297373a894 | 77 | /* Return with an error code */ |
mbed_official | 579:53297373a894 | 78 | return STATUS_ERR_BAUDRATE_UNAVAILABLE; |
mbed_official | 579:53297373a894 | 79 | } else { |
mbed_official | 579:53297373a894 | 80 | *baudvalue = baud_calculated; |
mbed_official | 579:53297373a894 | 81 | return STATUS_OK; |
mbed_official | 579:53297373a894 | 82 | } |
mbed_official | 579:53297373a894 | 83 | } |
mbed_official | 579:53297373a894 | 84 | |
mbed_official | 579:53297373a894 | 85 | /** |
mbed_official | 579:53297373a894 | 86 | * \internal Calculate asynchronous baudrate value (UART) |
mbed_official | 579:53297373a894 | 87 | */ |
mbed_official | 579:53297373a894 | 88 | enum status_code _sercom_get_async_baud_val( |
mbed_official | 579:53297373a894 | 89 | const uint32_t baudrate, |
mbed_official | 579:53297373a894 | 90 | const uint32_t peripheral_clock, |
mbed_official | 579:53297373a894 | 91 | uint16_t *const baudval, |
mbed_official | 579:53297373a894 | 92 | enum sercom_asynchronous_operation_mode mode, |
mbed_official | 579:53297373a894 | 93 | enum sercom_asynchronous_sample_num sample_num) |
mbed_official | 579:53297373a894 | 94 | { |
mbed_official | 579:53297373a894 | 95 | /* Temporary variables */ |
mbed_official | 579:53297373a894 | 96 | uint64_t ratio = 0; |
mbed_official | 579:53297373a894 | 97 | uint64_t scale = 0; |
mbed_official | 579:53297373a894 | 98 | uint64_t baud_calculated = 0; |
mbed_official | 579:53297373a894 | 99 | uint8_t baud_fp; |
mbed_official | 579:53297373a894 | 100 | uint32_t baud_int = 0; |
mbed_official | 579:53297373a894 | 101 | uint64_t temp1, temp2; |
mbed_official | 579:53297373a894 | 102 | |
mbed_official | 579:53297373a894 | 103 | /* Check if the baudrate is outside of valid range */ |
mbed_official | 579:53297373a894 | 104 | if ((baudrate * sample_num) > peripheral_clock) { |
mbed_official | 579:53297373a894 | 105 | /* Return with error code */ |
mbed_official | 579:53297373a894 | 106 | return STATUS_ERR_BAUDRATE_UNAVAILABLE; |
mbed_official | 579:53297373a894 | 107 | } |
mbed_official | 579:53297373a894 | 108 | |
mbed_official | 579:53297373a894 | 109 | if(mode == SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC) { |
mbed_official | 579:53297373a894 | 110 | /* Calculate the BAUD value */ |
mbed_official | 579:53297373a894 | 111 | temp1 = ((sample_num * (uint64_t)baudrate) << SHIFT); |
mbed_official | 579:53297373a894 | 112 | ratio = long_division(temp1, peripheral_clock); |
mbed_official | 579:53297373a894 | 113 | scale = ((uint64_t)1 << SHIFT) - ratio; |
mbed_official | 579:53297373a894 | 114 | baud_calculated = (65536 * scale) >> SHIFT; |
mbed_official | 579:53297373a894 | 115 | } else if(mode == SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL) { |
mbed_official | 579:53297373a894 | 116 | for(baud_fp = 0; baud_fp < BAUD_FP_MAX; baud_fp++) { |
mbed_official | 579:53297373a894 | 117 | temp1 = BAUD_FP_MAX * (uint64_t)peripheral_clock; |
mbed_official | 579:53297373a894 | 118 | temp2 = ((uint64_t)baudrate * sample_num); |
mbed_official | 579:53297373a894 | 119 | baud_int = long_division(temp1, temp2); |
mbed_official | 579:53297373a894 | 120 | baud_int -= baud_fp; |
mbed_official | 579:53297373a894 | 121 | baud_int = baud_int / BAUD_FP_MAX; |
mbed_official | 579:53297373a894 | 122 | if(baud_int < BAUD_INT_MAX) { |
mbed_official | 579:53297373a894 | 123 | break; |
mbed_official | 579:53297373a894 | 124 | } |
mbed_official | 579:53297373a894 | 125 | } |
mbed_official | 579:53297373a894 | 126 | if(baud_fp == BAUD_FP_MAX) { |
mbed_official | 579:53297373a894 | 127 | return STATUS_ERR_BAUDRATE_UNAVAILABLE; |
mbed_official | 579:53297373a894 | 128 | } |
mbed_official | 579:53297373a894 | 129 | baud_calculated = baud_int | (baud_fp << 13); |
mbed_official | 579:53297373a894 | 130 | } |
mbed_official | 579:53297373a894 | 131 | |
mbed_official | 579:53297373a894 | 132 | *baudval = baud_calculated; |
mbed_official | 579:53297373a894 | 133 | return STATUS_OK; |
mbed_official | 579:53297373a894 | 134 | } |
mbed_official | 579:53297373a894 | 135 | #endif |
mbed_official | 579:53297373a894 | 136 | |
mbed_official | 579:53297373a894 | 137 | /** |
mbed_official | 579:53297373a894 | 138 | * \brief Set GCLK channel to generator. |
mbed_official | 579:53297373a894 | 139 | * |
mbed_official | 579:53297373a894 | 140 | * This will set the appropriate GCLK channel to the requested GCLK generator. |
mbed_official | 579:53297373a894 | 141 | * This will set the generator for all SERCOM instances, and the user will thus |
mbed_official | 579:53297373a894 | 142 | * only be able to set the same generator that has previously been set, if any. |
mbed_official | 579:53297373a894 | 143 | * |
mbed_official | 579:53297373a894 | 144 | * After the generator has been set the first time, the generator can be changed |
mbed_official | 579:53297373a894 | 145 | * using the \c force_change flag. |
mbed_official | 579:53297373a894 | 146 | * |
mbed_official | 579:53297373a894 | 147 | * \param[in] generator_source The generator to use for SERCOM. |
mbed_official | 579:53297373a894 | 148 | * \param[in] force_change Force change the generator. |
mbed_official | 579:53297373a894 | 149 | * |
mbed_official | 579:53297373a894 | 150 | * \return Status code indicating the GCLK generator change operation. |
mbed_official | 579:53297373a894 | 151 | * \retval STATUS_OK If the generator update request was |
mbed_official | 579:53297373a894 | 152 | * successful. |
mbed_official | 579:53297373a894 | 153 | * \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured |
mbed_official | 579:53297373a894 | 154 | * and the new configuration was not |
mbed_official | 579:53297373a894 | 155 | * forced. |
mbed_official | 579:53297373a894 | 156 | */ |
mbed_official | 579:53297373a894 | 157 | enum status_code sercom_set_gclk_generator( |
mbed_official | 579:53297373a894 | 158 | const enum gclk_generator generator_source, |
mbed_official | 579:53297373a894 | 159 | const bool force_change) |
mbed_official | 579:53297373a894 | 160 | { |
mbed_official | 579:53297373a894 | 161 | /* Check if valid option. */ |
mbed_official | 579:53297373a894 | 162 | if (!_sercom_config.generator_is_set || force_change) { |
mbed_official | 579:53297373a894 | 163 | /* Create and fill a GCLK configuration structure for the new config. */ |
mbed_official | 579:53297373a894 | 164 | struct system_gclk_chan_config gclk_chan_conf; |
mbed_official | 579:53297373a894 | 165 | system_gclk_chan_get_config_defaults(&gclk_chan_conf); |
mbed_official | 579:53297373a894 | 166 | gclk_chan_conf.source_generator = generator_source; |
mbed_official | 579:53297373a894 | 167 | system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf); |
mbed_official | 579:53297373a894 | 168 | system_gclk_chan_enable(SERCOM_GCLK_ID); |
mbed_official | 579:53297373a894 | 169 | |
mbed_official | 579:53297373a894 | 170 | /* Save config. */ |
mbed_official | 579:53297373a894 | 171 | _sercom_config.generator_source = generator_source; |
mbed_official | 579:53297373a894 | 172 | _sercom_config.generator_is_set = true; |
mbed_official | 579:53297373a894 | 173 | |
mbed_official | 579:53297373a894 | 174 | return STATUS_OK; |
mbed_official | 579:53297373a894 | 175 | } else if (generator_source == _sercom_config.generator_source) { |
mbed_official | 579:53297373a894 | 176 | /* Return status OK if same config. */ |
mbed_official | 579:53297373a894 | 177 | return STATUS_OK; |
mbed_official | 579:53297373a894 | 178 | } |
mbed_official | 579:53297373a894 | 179 | |
mbed_official | 579:53297373a894 | 180 | /* Return invalid config to already initialized GCLK. */ |
mbed_official | 579:53297373a894 | 181 | return STATUS_ERR_ALREADY_INITIALIZED; |
mbed_official | 579:53297373a894 | 182 | } |
mbed_official | 579:53297373a894 | 183 | |
mbed_official | 579:53297373a894 | 184 | /** \internal |
mbed_official | 579:53297373a894 | 185 | * Creates a switch statement case entry to convert a SERCOM instance and pad |
mbed_official | 579:53297373a894 | 186 | * index to the default SERCOM pad MUX setting. |
mbed_official | 579:53297373a894 | 187 | */ |
mbed_official | 579:53297373a894 | 188 | #define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \ |
mbed_official | 579:53297373a894 | 189 | case (uintptr_t)SERCOM##n: \ |
mbed_official | 579:53297373a894 | 190 | switch (pad) { \ |
mbed_official | 579:53297373a894 | 191 | case 0: \ |
mbed_official | 579:53297373a894 | 192 | return SERCOM##n##_PAD0_DEFAULT; \ |
mbed_official | 579:53297373a894 | 193 | case 1: \ |
mbed_official | 579:53297373a894 | 194 | return SERCOM##n##_PAD1_DEFAULT; \ |
mbed_official | 579:53297373a894 | 195 | case 2: \ |
mbed_official | 579:53297373a894 | 196 | return SERCOM##n##_PAD2_DEFAULT; \ |
mbed_official | 579:53297373a894 | 197 | case 3: \ |
mbed_official | 579:53297373a894 | 198 | return SERCOM##n##_PAD3_DEFAULT; \ |
mbed_official | 579:53297373a894 | 199 | } \ |
mbed_official | 579:53297373a894 | 200 | break; |
mbed_official | 579:53297373a894 | 201 | |
mbed_official | 579:53297373a894 | 202 | /** |
mbed_official | 579:53297373a894 | 203 | * \internal Gets the default PAD pinout for a given SERCOM. |
mbed_official | 579:53297373a894 | 204 | * |
mbed_official | 579:53297373a894 | 205 | * Returns the pinmux settings for the given SERCOM and pad. This is used |
mbed_official | 579:53297373a894 | 206 | * for default configuration of pins. |
mbed_official | 579:53297373a894 | 207 | * |
mbed_official | 579:53297373a894 | 208 | * \param[in] sercom_module Pointer to the SERCOM module |
mbed_official | 579:53297373a894 | 209 | * \param[in] pad PAD to get default pinout for |
mbed_official | 579:53297373a894 | 210 | * |
mbed_official | 579:53297373a894 | 211 | * \returns The default pinmux for the given SERCOM instance and PAD |
mbed_official | 579:53297373a894 | 212 | * |
mbed_official | 579:53297373a894 | 213 | */ |
mbed_official | 579:53297373a894 | 214 | uint32_t _sercom_get_default_pad( |
mbed_official | 579:53297373a894 | 215 | Sercom *const sercom_module, |
mbed_official | 579:53297373a894 | 216 | const uint8_t pad) |
mbed_official | 579:53297373a894 | 217 | { |
mbed_official | 579:53297373a894 | 218 | switch ((uintptr_t)sercom_module) { |
mbed_official | 579:53297373a894 | 219 | /* Auto-generate a lookup table for the default SERCOM pad defaults */ |
mbed_official | 579:53297373a894 | 220 | MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad) |
mbed_official | 579:53297373a894 | 221 | } |
mbed_official | 579:53297373a894 | 222 | |
mbed_official | 579:53297373a894 | 223 | Assert(false); |
mbed_official | 579:53297373a894 | 224 | return 0; |
mbed_official | 579:53297373a894 | 225 | } |
mbed_official | 579:53297373a894 | 226 | |
mbed_official | 579:53297373a894 | 227 | /** |
mbed_official | 579:53297373a894 | 228 | * \internal |
mbed_official | 579:53297373a894 | 229 | * Find index of given instance. |
mbed_official | 579:53297373a894 | 230 | * |
mbed_official | 579:53297373a894 | 231 | * \param[in] sercom_instance Instance pointer. |
mbed_official | 579:53297373a894 | 232 | * |
mbed_official | 579:53297373a894 | 233 | * \return Index of given instance. |
mbed_official | 579:53297373a894 | 234 | */ |
mbed_official | 579:53297373a894 | 235 | uint8_t _sercom_get_sercom_inst_index( |
mbed_official | 579:53297373a894 | 236 | Sercom *const sercom_instance) |
mbed_official | 579:53297373a894 | 237 | { |
mbed_official | 579:53297373a894 | 238 | /* Save all available SERCOM instances for compare. */ |
mbed_official | 579:53297373a894 | 239 | Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS; |
mbed_official | 579:53297373a894 | 240 | |
mbed_official | 579:53297373a894 | 241 | /* Find index for sercom instance. */ |
mbed_official | 579:53297373a894 | 242 | for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) { |
mbed_official | 579:53297373a894 | 243 | if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) { |
mbed_official | 579:53297373a894 | 244 | return i; |
mbed_official | 579:53297373a894 | 245 | } |
mbed_official | 579:53297373a894 | 246 | } |
mbed_official | 579:53297373a894 | 247 | |
mbed_official | 579:53297373a894 | 248 | /* Invalid data given. */ |
mbed_official | 579:53297373a894 | 249 | Assert(false); |
mbed_official | 579:53297373a894 | 250 | return 0; |
mbed_official | 579:53297373a894 | 251 | } |