/* * Copyright (C) 2014 Hamburg University of Applied Sciences * 2014 PHYTEC Messtechnik GmbH * 2015 Eistec AB * 2016 Freie UniversitÀt Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @brief SPI bus scaler computation * * This helper tool calculates the needed SPI scaler values for a given APB bus * clock speed. The result of the computation must be placed in a board's * periph_conf.h for quick reference by the SPI drivers. * * @author Peter Kietzmann * @author Johann Fischer * @author Joakim NohlgÄrd * @author Hauke Petersen * * @} */ #include #include #include /** * @brief Targeted SPI bus speed values (pre-defined by RIOT) */ static uint32_t targets[] = { 100000, 400000, 1000000, 5000000, 10000000 }; /** * @brief Helper function for finding optimal baud rate scalers. * * Find the prescaler and scaler settings that will yield a clock frequency * as close as possible (but not above) the target frequency, given the module * runs at module_clock Hz. * * Hardware properties (Baud rate configuration): * Possible prescalers: 2, 3, 5, 7 * Possible scalers: 2, 4, 6 (sic!), 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 * * SCK baud rate = (f_BUS/PBR) x [(1+DBR)/BR] * * where PBR is the prescaler, BR is the scaler, DBR is the Double BaudRate bit. * * @note We are not using the DBR bit because it may affect the SCK duty cycle. * * @param module_clock Module clock frequency (e.g. F_BUS) * @param target_clock Desired baud rate * @param closest_prescaler pointer where to write the optimal prescaler index. * @param closest_scaler pointer where to write the optimal scaler index. * * @return The actual achieved frequency on success * @return Less than 0 on error. */ static long find_closest_baudrate_scalers(const uint32_t module_clock, const long target_clock, uint8_t *closest_prescaler, uint8_t *closest_scaler) { uint8_t i; uint8_t k; long freq; static const uint8_t num_scalers = 16; static const uint8_t num_prescalers = 4; static const int br_scalers[16] = { 2, 4, 6, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; static const int br_prescalers[4] = {2, 3, 5, 7}; long closest_frequency = -1; /* Test all combinations until we arrive close to the target clock */ for (i = 0; i < num_prescalers; ++i) { for (k = 0; k < num_scalers; ++k) { freq = module_clock / (br_scalers[k] * br_prescalers[i]); if (freq <= target_clock) { /* Found closest lower frequency at this prescaler setting, * compare to the best result */ if (closest_frequency < freq) { closest_frequency = freq; *closest_scaler = k; *closest_prescaler = i; } break; } } } if (closest_frequency < 0) { /* Error, no solution found, this line is never reachable with current * hardware settings unless a _very_ low target clock is requested. * (scaler_max * prescaler_max) = 229376 => target_min@100MHz = 435 Hz*/ return -1; } return closest_frequency; } /** * @brief Helper function for finding optimal delay scalers. * * Find the prescaler and scaler settings that will yield a delay timing * as close as possible (but not shorter than) the target delay, given the * module runs at module_clock Hz. * * Hardware properties (delay configuration): * Possible prescalers: 1, 3, 5, 7 * Possible scalers: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 * * delay = (1/f_BUS) x prescaler x scaler * * Because we want to do this using only integers, the target_freq parameter is * the reciprocal of the delay time. * * @param module_clock Module clock frequency (e.g. F_BUS) * @param target_freq Reciprocal (i.e. 1/t [Hz], frequency) of the desired delay time. * @param closest_prescaler pointer where to write the optimal prescaler index. * @param closest_scaler pointer where to write the optimal scaler index. * * @return The actual achieved frequency on success * @return Less than 0 on error. */ static long find_closest_delay_scalers(const uint32_t module_clock, const long target_freq, uint8_t *closest_prescaler, uint8_t *closest_scaler) { uint8_t i; uint8_t k; long freq; int prescaler; int scaler; static const uint8_t num_scalers = 16; static const uint8_t num_prescalers = 4; long closest_frequency = -1; /* Test all combinations until we arrive close to the target clock */ for (i = 0; i < num_prescalers; ++i) { for (k = 0; k < num_scalers; ++k) { prescaler = (i * 2) + 1; scaler = (1 << (k + 1)); /* 2^(k+1) */ freq = module_clock / (prescaler * scaler); if (freq <= target_freq) { /* Found closest lower frequency at this prescaler setting, * compare to the best result */ if (closest_frequency < freq) { closest_frequency = freq; *closest_scaler = k; *closest_prescaler = i; } break; } } } if (closest_frequency < 0) { /* Error, no solution found, this line is never reachable with current * hardware settings unless a _very_ low target clock is requested. * (scaler_max * prescaler_max) = 458752 */ return -1; } return closest_frequency; } int main(int argc, char **argv) { uint32_t modclk; int i; if (argc != 2) { printf("usage: %s \n", argv[0]); return 1; } modclk = atoi(argv[1]); if (modclk == 0) { printf("error: invalid input value\n"); return 1; } printf("\nCalculating SPI clock scalers for a module clock of: %iHz\n\n", (int)modclk); puts("static const uint32_t spi_clk_config[] = {"); for (i = 0; i < ARRAY_SIZE(targets); i++) { uint8_t tmp, ptmp; long res; /* bus clock */ res = find_closest_baudrate_scalers(modclk, targets[i], &ptmp, &tmp); if (res < 0) { puts("error: no applicable bus clock scalers could be found!"); return 1; } puts(" ("); printf(" SPI_CTAR_PBR(%i) | SPI_CTAR_BR(%i) | /* -> %iHz */\n", (int)ptmp, (int)tmp, (int)res); /* t_csc: chip select to fist clock signal delay */ if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { puts("error: no applicable delay values for t_csc found\n"); return 1; } printf(" SPI_CTAR_PCSSCK(%i) | SPI_CTAR_CSSCK(%i) |\n", (int)ptmp, (int)tmp); /* t_asc: delay after last clock signal to release of chip select */ if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { puts("error: no applicable delay values for t_asc found\n"); return 1; } printf(" SPI_CTAR_PASC(%i) | SPI_CTAR_ASC(%i) |\n", (int)ptmp, (int)tmp); /* t_psc: delay between release and next assertion of chip select */ if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) { puts("error: no applicable delay values for t_csc found\n"); return 1; } printf(" SPI_CTAR_PDT(%i) | SPI_CTAR_DT(%i)\n", (int)ptmp, (int)tmp); if (i == ARRAY_SIZE(targets) - 1) { puts(" )"); } else { puts(" ),"); } } puts("};"); return 0; }