|
30 | 30 | #include "mbed_assert.h"
|
31 | 31 | #include "mbed_error.h"
|
32 | 32 | #include "mbed_debug.h"
|
| 33 | +#include "mbed_critical.h" |
| 34 | +#include "mbed_wait_api.h" |
33 | 35 | #include "spi_api.h"
|
34 | 36 |
|
35 | 37 | #if DEVICE_SPI
|
@@ -602,6 +604,50 @@ static const uint32_t baudrate_prescaler_table[] = {SPI_BAUDRATEPRESCALER_2,
|
602 | 604 | SPI_BAUDRATEPRESCALER_256
|
603 | 605 | };
|
604 | 606 |
|
| 607 | +/** |
| 608 | + * Convert SPI_BAUDRATEPRESCALER_<X> constant into numeric prescaler rank. |
| 609 | + */ |
| 610 | +static uint8_t spi_get_baudrate_prescaler_rank(uint32_t value) |
| 611 | +{ |
| 612 | + switch (value) { |
| 613 | + case SPI_BAUDRATEPRESCALER_2: |
| 614 | + return 0; |
| 615 | + case SPI_BAUDRATEPRESCALER_4: |
| 616 | + return 1; |
| 617 | + case SPI_BAUDRATEPRESCALER_8: |
| 618 | + return 2; |
| 619 | + case SPI_BAUDRATEPRESCALER_16: |
| 620 | + return 3; |
| 621 | + case SPI_BAUDRATEPRESCALER_32: |
| 622 | + return 4; |
| 623 | + case SPI_BAUDRATEPRESCALER_64: |
| 624 | + return 5; |
| 625 | + case SPI_BAUDRATEPRESCALER_128: |
| 626 | + return 6; |
| 627 | + case SPI_BAUDRATEPRESCALER_256: |
| 628 | + return 7; |
| 629 | + default: |
| 630 | + return 0xFF; |
| 631 | + } |
| 632 | +} |
| 633 | + |
| 634 | +/** |
| 635 | + * Get actual SPI baudrate. |
| 636 | + * |
| 637 | + * It may differ from a value that is passed to the ::spi_frequency function. |
| 638 | + */ |
| 639 | +int spi_get_baudrate(spi_t *obj) |
| 640 | +{ |
| 641 | + struct spi_s *spiobj = SPI_S(obj); |
| 642 | + SPI_HandleTypeDef *handle = &(spiobj->handle); |
| 643 | + |
| 644 | + int freq = spi_get_clock_freq(obj); |
| 645 | + uint8_t baudrate_rank = spi_get_baudrate_prescaler_rank(handle->Init.BaudRatePrescaler); |
| 646 | + MBED_ASSERT(baudrate_rank != 0xFF); |
| 647 | + return freq >> (baudrate_rank + 1); |
| 648 | +} |
| 649 | + |
| 650 | + |
605 | 651 | void spi_frequency(spi_t *obj, int hz)
|
606 | 652 | {
|
607 | 653 | struct spi_s *spiobj = SPI_S(obj);
|
@@ -823,6 +869,29 @@ static inline void msp_wait_readable(spi_t *obj)
|
823 | 869 | while (!msp_readable(obj));
|
824 | 870 | }
|
825 | 871 |
|
| 872 | +/** |
| 873 | + * Check if SPI master interface is busy. |
| 874 | + * |
| 875 | + * @param obj |
| 876 | + * @return 0 - SPI isn't busy, non-zero - SPI is busy |
| 877 | + */ |
| 878 | +static inline int msp_busy(spi_t *obj) |
| 879 | +{ |
| 880 | +#if TARGET_STM32H7 |
| 881 | + return !(int)LL_SPI_IsActiveFlag_TXC(SPI_INST(obj)); |
| 882 | +#else /* TARGET_STM32H7 */ |
| 883 | + return (int)LL_SPI_IsActiveFlag_BSY(SPI_INST(obj)); |
| 884 | +#endif /* TARGET_STM32H7 */ |
| 885 | +} |
| 886 | + |
| 887 | +/** |
| 888 | + * Wait till SPI master interface isn't busy. |
| 889 | + */ |
| 890 | +static inline void msp_wait_not_busy(spi_t *obj) |
| 891 | +{ |
| 892 | + while (msp_busy(obj)); |
| 893 | +} |
| 894 | + |
826 | 895 | /**
|
827 | 896 | * Write data to SPI master interface.
|
828 | 897 | */
|
@@ -855,13 +924,126 @@ static inline int msp_read_data(spi_t *obj, int bitshift)
|
855 | 924 | }
|
856 | 925 | }
|
857 | 926 |
|
| 927 | +/** |
| 928 | + * Transmit and receive SPI data in bidirectional mode. |
| 929 | + * |
| 930 | + * @param obj spi object |
| 931 | + * @param tx_buffer byte-array of data to write to the device |
| 932 | + * @param tx_length number of bytes to write, may be zero |
| 933 | + * @param rx_buffer byte-array of data to read from the device |
| 934 | + * @param rx_length number of bytes to read, may be zero |
| 935 | + * @return number of transmitted and received bytes or negative code in case of error. |
| 936 | + */ |
| 937 | +static int spi_master_one_wire_transfer(spi_t *obj, const char *tx_buffer, int tx_length, |
| 938 | + char *rx_buffer, int rx_length) |
| 939 | +{ |
| 940 | + struct spi_s *spiobj = SPI_S(obj); |
| 941 | + SPI_HandleTypeDef *handle = &(spiobj->handle); |
| 942 | + const int bitshift = datasize_to_transfer_bitshift(handle->Init.DataSize); |
| 943 | + MBED_ASSERT(bitshift >= 0); |
| 944 | + |
| 945 | + /* Ensure that spi is disabled */ |
| 946 | + LL_SPI_Disable(SPI_INST(obj)); |
| 947 | + |
| 948 | + /* Transmit data */ |
| 949 | + if (tx_length) { |
| 950 | + LL_SPI_SetTransferDirection(SPI_INST(obj), LL_SPI_HALF_DUPLEX_TX); |
| 951 | +#if TARGET_STM32H7 |
| 952 | + /* Set transaction size */ |
| 953 | + LL_SPI_SetTransferSize(SPI_INST(obj), tx_length); |
| 954 | +#endif /* TARGET_STM32H7 */ |
| 955 | + LL_SPI_Enable(SPI_INST(obj)); |
| 956 | +#if TARGET_STM32H7 |
| 957 | + /* Master transfer start */ |
| 958 | + LL_SPI_StartMasterTransfer(SPI_INST(obj)); |
| 959 | +#endif /* TARGET_STM32H7 */ |
| 960 | + |
| 961 | + for (int i = 0; i < tx_length; i++) { |
| 962 | + msp_wait_writable(obj); |
| 963 | + msp_write_data(obj, tx_buffer[i], bitshift); |
| 964 | + } |
| 965 | + |
| 966 | + /* Wait end of transaction */ |
| 967 | + msp_wait_not_busy(obj); |
| 968 | + |
| 969 | + LL_SPI_Disable(SPI_INST(obj)); |
| 970 | + |
| 971 | +#if TARGET_STM32H7 |
| 972 | + /* Clear transaction flags */ |
| 973 | + LL_SPI_ClearFlag_EOT(SPI_INST(obj)); |
| 974 | + LL_SPI_ClearFlag_TXTF(SPI_INST(obj)); |
| 975 | + /* Reset transaction size */ |
| 976 | + LL_SPI_SetTransferSize(SPI_INST(obj), 0); |
| 977 | +#endif /* TARGET_STM32H7 */ |
| 978 | + } |
| 979 | + |
| 980 | + /* Receive data */ |
| 981 | + if (rx_length) { |
| 982 | + LL_SPI_SetTransferDirection(SPI_INST(obj), LL_SPI_HALF_DUPLEX_RX); |
| 983 | +#if TARGET_STM32H7 |
| 984 | + /* Set transaction size and run SPI */ |
| 985 | + LL_SPI_SetTransferSize(SPI_INST(obj), rx_length); |
| 986 | + LL_SPI_Enable(SPI_INST(obj)); |
| 987 | + LL_SPI_StartMasterTransfer(SPI_INST(obj)); |
| 988 | + |
| 989 | + /* Receive data */ |
| 990 | + for (int i = 0; i < rx_length; i++) { |
| 991 | + msp_wait_readable(obj); |
| 992 | + rx_buffer[i] = msp_read_data(obj, bitshift); |
| 993 | + } |
| 994 | + |
| 995 | + /* Stop SPI */ |
| 996 | + LL_SPI_Disable(SPI_INST(obj)); |
| 997 | + /* Clear transaction flags */ |
| 998 | + LL_SPI_ClearFlag_EOT(SPI_INST(obj)); |
| 999 | + LL_SPI_ClearFlag_TXTF(SPI_INST(obj)); |
| 1000 | + /* Reset transaction size */ |
| 1001 | + LL_SPI_SetTransferSize(SPI_INST(obj), 0); |
| 1002 | + |
| 1003 | +#else /* TARGET_STM32H7 */ |
| 1004 | + /* Unlike STM32H7 other STM32 families generates SPI Clock signal continuously in half-duplex receive mode |
| 1005 | + * till SPI is enabled. To stop clock generation a SPI should be disabled during last frame receiving, |
| 1006 | + * after generation at least one SPI clock cycle. It causes necessity of critical section usage. |
| 1007 | + * So the following consequences of steps is used to receive each byte: |
| 1008 | + * 1. Enter into critical section. |
| 1009 | + * 2. Enable SPI. |
| 1010 | + * 3. Wait one SPI clock cycle. |
| 1011 | + * 4. Disable SPI. |
| 1012 | + * 5. Wait full byte receiving. |
| 1013 | + * 6. Read byte. |
| 1014 | + * It gives some overhead, but gives stable byte reception without dummy reads and |
| 1015 | + * short delay of critical section holding. |
| 1016 | + */ |
| 1017 | + |
| 1018 | + /* get estimation about one SPI clock cycle */ |
| 1019 | + uint32_t baudrate_period_ns = 1000000000 / spi_get_baudrate(obj); |
| 1020 | + |
| 1021 | + for (int i = 0; i < rx_length; i++) { |
| 1022 | + core_util_critical_section_enter(); |
| 1023 | + LL_SPI_Enable(SPI_INST(obj)); |
| 1024 | + /* Wait single SPI clock cycle. */ |
| 1025 | + wait_ns(baudrate_period_ns); |
| 1026 | + LL_SPI_Disable(SPI_INST(obj)); |
| 1027 | + core_util_critical_section_exit(); |
| 1028 | + |
| 1029 | + msp_wait_readable(obj); |
| 1030 | + rx_buffer[i] = msp_read_data(obj, bitshift); |
| 1031 | + } |
| 1032 | + |
| 1033 | +#endif /* TARGET_STM32H7 */ |
| 1034 | + } |
| 1035 | + |
| 1036 | + return rx_length + tx_length; |
| 1037 | +} |
| 1038 | + |
858 | 1039 | int spi_master_write(spi_t *obj, int value)
|
859 | 1040 | {
|
860 | 1041 | struct spi_s *spiobj = SPI_S(obj);
|
861 | 1042 | SPI_HandleTypeDef *handle = &(spiobj->handle);
|
862 | 1043 |
|
863 | 1044 | if (handle->Init.Direction == SPI_DIRECTION_1LINE) {
|
864 |
| - return HAL_SPI_Transmit(handle, (uint8_t *)&value, 1, TIMEOUT_1_BYTE); |
| 1045 | + int result = spi_master_one_wire_transfer(obj, (const char *)&value, 1, NULL, 0); |
| 1046 | + return result == 1 ? HAL_OK : HAL_ERROR; |
865 | 1047 | }
|
866 | 1048 | const int bitshift = datasize_to_transfer_bitshift(handle->Init.DataSize);
|
867 | 1049 | MBED_ASSERT(bitshift >= 0);
|
@@ -910,18 +1092,11 @@ int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length,
|
910 | 1092 | }
|
911 | 1093 | }
|
912 | 1094 | } else {
|
913 |
| - /* In case of 1 WIRE only, first handle TX, then Rx */ |
914 |
| - if (tx_length != 0) { |
915 |
| - if (HAL_OK != HAL_SPI_Transmit(handle, (uint8_t *)tx_buffer, tx_length, tx_length * TIMEOUT_1_BYTE)) { |
916 |
| - /* report an error */ |
917 |
| - total = 0; |
918 |
| - } |
919 |
| - } |
920 |
| - if (rx_length != 0) { |
921 |
| - if (HAL_OK != HAL_SPI_Receive(handle, (uint8_t *)rx_buffer, rx_length, rx_length * TIMEOUT_1_BYTE)) { |
922 |
| - /* report an error */ |
923 |
| - total = 0; |
924 |
| - } |
| 1095 | + /* 1 wire case */ |
| 1096 | + int result = spi_master_one_wire_transfer(obj, tx_buffer, tx_length, rx_buffer, rx_length); |
| 1097 | + if (result != tx_length + rx_length) { |
| 1098 | + /* report an error */ |
| 1099 | + total = 0; |
925 | 1100 | }
|
926 | 1101 | }
|
927 | 1102 |
|
|
0 commit comments